Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds functionality for patch-based configuration #839

Merged
merged 12 commits into from
Sep 27, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ One just needs to extend the case-study file of a project with a yaml document t
.. code-block:: yaml

---
config_type: PlainCommandlineConfiguration
0: '["--foo", "--bar"]'
1: '["--foo"]'
...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ stages:
version: 0
...
---
config_type: PlainCommandlineConfiguration
0: '["--compress", "--mem", "10", "8"]'
1: '["--compress", "--mem", "300", "8"]'
...
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ stages:
config_ids: [1]
version: 0
---
config_type: PlainCommandlineConfiguration
0: '["--foo", "--bar"]'
1: '["--foo"]'
1 change: 1 addition & 0 deletions tests/paper/test_case_study.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
commit_id: 494
...
---
config_type: ConfigurationImpl
0: '{"foo": true, "bar": false, "bazz": "bazz-value", "buzz": "None"}'
1: '{}'
2: '{}'
Expand Down
125 changes: 110 additions & 15 deletions tests/provider/test_patch_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,57 +184,120 @@ def setUpClass(cls) -> None:
"Test-ABCD",
"",
path=Path("test.patch"),
tags={"A", "B", "C", "D"}
tags={"A", "B", "C", "D"},
feature_tags={"F_A", "F_B", "F_C", "F_D"}
),
Patch("TEST", "Test-A", "", path=Path("test.patch"), tags={"A"}),
Patch("TEST", "Test-B", "", path=Path("test.patch"), tags={"B"}),
Patch("TEST", "Test-C", "", path=Path("test.patch"), tags={"C"}),
Patch("TEST", "Test-D", "", path=Path("test.patch"), tags={"D"}),
Patch(
"TEST", "Test-AB", "", path=Path("test.patch"), tags={"A", "B"}
"TEST",
"Test-A",
"",
path=Path("test.patch"),
tags={"A"},
feature_tags={"F_A"}
),
Patch(
"TEST",
"Test-B",
"",
path=Path("test.patch"),
tags={"B"},
feature_tags={"F_B"}
),
Patch(
"TEST",
"Test-C",
"",
path=Path("test.patch"),
tags={"C"},
feature_tags={"F_C"}
),
Patch(
"TEST", "Test-AC", "", path=Path("test.patch"), tags={"A", "C"}
"TEST",
"Test-D",
"",
path=Path("test.patch"),
tags={"D"},
feature_tags={"F_D"}
),
Patch(
"TEST",
"Test-AB",
"",
path=Path("test.patch"),
tags={"A", "B"},
feature_tags={"F_A", "F_B"}
),
Patch(
"TEST",
"Test-AC",
"",
path=Path("test.patch"),
tags={"A", "C"},
feature_tags={"F_A", "F_C"}
),
Patch(
"TEST", "Test-AD", "", path=Path("test.patch"), tags={"A", "D"}
"TEST",
"Test-AD",
"",
path=Path("test.patch"),
tags={"A", "D"},
feature_tags={"F_A", "F_D"}
),
Patch(
"TEST", "Test-BC", "", path=Path("test.patch"), tags={"B", "C"}
"TEST",
"Test-BC",
"",
path=Path("test.patch"),
tags={"B", "C"},
feature_tags={"F_B", "F_C"}
),
Patch(
"TEST", "Test-BD", "", path=Path("test.patch"), tags={"B", "D"}
"TEST",
"Test-BD",
"",
path=Path("test.patch"),
tags={"B", "D"},
feature_tags={"F_B", "F_D"}
),
Patch(
"TEST", "Test-CD", "", path=Path("test.patch"), tags={"C", "D"}
"TEST",
"Test-CD",
"",
path=Path("test.patch"),
tags={"C", "D"},
feature_tags={"F_C", "F_D"}
),
Patch(
"TEST",
"Test-ABC",
"",
path=Path("test.patch"),
tags={"A", "B", "C"}
tags={"A", "B", "C"},
feature_tags={"F_A", "F_B", "F_C"}
),
Patch(
"TEST",
"Test-ABD",
"",
path=Path("test.patch"),
tags={"A", "B", "D"}
tags={"A", "B", "D"},
feature_tags={"F_A", "F_B", "F_D"}
),
Patch(
"TEST",
"Test-ACD",
"",
path=Path("test.patch"),
tags={"A", "C", "D"}
tags={"A", "C", "D"},
feature_tags={"F_A", "F_C", "F_D"}
),
Patch(
"TEST",
"Test-BCD",
"",
path=Path("test.patch"),
tags={"B", "C", "D"}
tags={"B", "C", "D"},
feature_tags={"F_B", "F_C", "F_D"}
),
}

Expand Down Expand Up @@ -311,6 +374,38 @@ def test_any_of_multiple_tags(self):
for patch in patches:
any([tag in patch.tags for tag in tags])

def test_all_of_single_feature_tag(self):
for tag in {"F_A", "F_B", "F_C", "F_D"}:
patches = self.patchSet.all_of_features([tag])
self.assertEqual(8, len(patches))

def test_all_of_multiple_feature_tags(self):
tags_count = {("F_A", "F_B"): 4,
("F_C", "F_B"): 4,
("F_D", "F_B"): 4,
("F_A", "F_B", "F_C"): 2,
("F_A", "F_B", "F_C", "F_D"): 1}

for tags in tags_count:
patches = self.patchSet.all_of_features(tags)
self.assertEqual(tags_count[tags], len(patches))

def test_any_of_single_feature_tag(self):
for tag in {"F_A", "F_B", "F_C", "F_D"}:
patches = self.patchSet.any_of_features([tag])
self.assertEqual(8, len(patches))

def test_any_of_multiple_feature_tags(self):
tags_count = {("F_A", "F_B"): 12,
("F_C", "F_B"): 12,
("F_D", "F_B"): 12,
("F_A", "F_B", "F_C"): 14,
("F_A", "F_B", "F_C", "F_D"): 15}

for tags in tags_count:
patches = self.patchSet.any_of_features(tags)
self.assertEqual(tags_count[tags], len(patches))

def test_patchset_intersection(self):
patches = self.patchSet["A"] & self.patchSet["B"]

Expand Down
46 changes: 46 additions & 0 deletions varats-core/varats/base/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,49 @@ def get_config_value(self, option_name: str) -> tp.Optional[tp.Any]:

def unfreeze(self) -> Configuration:
return self


class PatchConfiguration(Configuration):
"""Configuration class for projects where configuring is done by applying a
patch."""

def __init__(self, patch_feature_tags: tp.Set[str]):
self.__patch_feature_tags: tp.Set[ConfigurationOption] = {
ConfigurationOptionImpl(tag, tag) for tag in patch_feature_tags
}

@staticmethod
def create_configuration_from_str(config_str: str) -> Configuration:
patch_feature_tags = json.loads(config_str)
return PatchConfiguration(patch_feature_tags)

def add_config_option(self, option: ConfigurationOption) -> None:
self.__patch_feature_tags.add(option)

def set_config_option(self, option_name: str, value: tp.Any) -> None:
self.__patch_feature_tags = {
option for option in self.__patch_feature_tags
if option.name != option_name
}
self.add_config_option(ConfigurationOptionImpl(option_name, value))

def get_config_value(self, option_name: str) -> tp.Optional[tp.Any]:
filtered_options = filter(
lambda option: (option.name == option_name),
self.__patch_feature_tags
)
return any(filtered_options)

def options(self) -> tp.List[ConfigurationOption]:
return list(self.__patch_feature_tags)

def dump_to_string(self) -> str:
return ", ".join(
map(lambda option: str(option.value), self.__patch_feature_tags)
)

def freeze(self) -> FrozenConfiguration:
return FrozenConfiguration(deepcopy(self))

def unfreeze(self) -> Configuration:
return self
89 changes: 70 additions & 19 deletions varats-core/varats/experiment/experiment_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from collections import defaultdict
from pathlib import Path
from types import TracebackType
from typing import Protocol, runtime_checkable

from benchbuild import source
from benchbuild.experiment import Experiment
Expand All @@ -23,11 +22,17 @@
from plumbum.commands.base import BoundCommand

import varats.revision.revisions as revs
from varats.base.configuration import PlainCommandlineConfiguration
from varats.base.configuration import (
PlainCommandlineConfiguration,
PatchConfiguration,
Configuration,
)
from varats.experiment.steps.patch import ApplyPatch
from varats.paper.paper_config import get_paper_config
from varats.project.project_util import ProjectBinaryWrapper
from varats.project.sources import FeatureSource
from varats.project.varats_project import VProject
from varats.provider.patch.patch_provider import PatchSet, PatchProvider
from varats.report.report import (
BaseReport,
FileStatusExtension,
Expand Down Expand Up @@ -696,20 +701,12 @@ def get_current_config_id(project: VProject) -> tp.Optional[int]:
return None


def get_extra_config_options(project: VProject) -> tp.List[str]:
"""
Get extra program options that were specified in the particular
configuration of \a Project.

Args:
project: to get the extra options for

Returns:
list of command line options as string
"""
def get_config(
project: VProject, config_type: tp.Type[Configuration]
) -> tp.Optional[Configuration]:
config_id = get_current_config_id(project)
if config_id is None:
return []
return None

paper_config = get_paper_config()
case_studies = paper_config.get_case_studies(cs_name=project.name)
Expand All @@ -722,14 +719,68 @@ def get_extra_config_options(project: VProject) -> tp.List[str]:
case_study = case_studies[0]

config_map = load_configuration_map_for_case_study(
paper_config, case_study, PlainCommandlineConfiguration
paper_config, case_study, config_type
)

config = config_map.get_configuration(config_id)

if config is None:
raise AssertionError(
"Requested config id was not in the map, but should be"
)
return config


def get_extra_config_options(project: VProject) -> tp.List[str]:
"""
Get extra program options that were specified in the particular
configuration of \a Project.

Args:
project: to get the extra options for

Returns:
list of command line options as string
"""
config = get_config(project, PlainCommandlineConfiguration)
if not config:
return []
return list(map(lambda option: option.value, config.options()))


def get_config_patches(project: VProject) -> PatchSet:
"""
Get required patches for the particular configuration of \a Project.

Args:
project: to get the patches for

Returns:
list of patches
"""
config = get_config(project, PatchConfiguration)
if not config:
return PatchSet(set())

patch_provider = PatchProvider.create_provider_for_project(project)
revision = ShortCommitHash(project.revision.primary.version)
feature_tags = {opt.value for opt in config.options()}
patches = patch_provider.get_patches_for_revision(revision).all_of_features(
feature_tags
)

return patches


def get_config_patch_steps(project: VProject) -> tp.MutableSequence[Step]:
"""
Get a list of actions that apply all configuration patches to the project.

Args:
project: the project to be configured

Returns:
the actions that configure the project
"""
return list(
map(
lambda patch: ApplyPatch(project, patch),
get_config_patches(project)
)
)
Loading
Loading