Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@

- [boa] fix multi-output
- [boa] fix keep run_export and existing spec when existing spec is not simple
- [mambabuild] allow testing multiple recipes (thanks @gabm)
- [mambabuild] allow testing multiple recipes (thanks @gabm)
14 changes: 14 additions & 0 deletions boa/core/features.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
def extract_features(feature_string):
if feature_string and len(feature_string):
assert feature_string.startswith("[") and feature_string.endswith("]")
features = [f.strip() for f in feature_string[1:-1].split(",")]
else:
features = []

selected_features = {}
for f in features:
if f.startswith("~"):
selected_features[f[1:]] = False
else:
selected_features[f] = True
return selected_features
7 changes: 7 additions & 0 deletions boa/core/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ def get_value(self, in_key: str, default: Any = None, autotype=True) -> Any:
else:
return section.get(key, default)

def rendered_meta(self):
res = self.meta.copy()
for typ in res.get("requirements", tuple()):
res["requirements"][typ] = [x.final_pin for x in self.get_dependencies(typ)]

return res

@property
def source_provided(self):
return not bool(self.meta.get("source")) or (
Expand Down
13 changes: 5 additions & 8 deletions boa/core/recipe_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,11 @@ def _copy_output_recipe(m, dest_dir):


def output_yaml(metadata, filename=None, suppress_outputs=False):
local_metadata = metadata.copy()
if (
suppress_outputs
and local_metadata.is_output
and "outputs" in local_metadata.meta
):
del local_metadata.meta["outputs"]
output = yaml.dump((local_metadata.meta), default_flow_style=False, indent=4)
local_metadata = metadata.rendered_meta().copy()
if suppress_outputs and metadata.is_output and "outputs" in local_metadata:
del local_metadata["outputs"]

output = yaml.dump((local_metadata), default_flow_style=False, indent=4)
if filename:
if any(sep in filename for sep in ("\\", "/")):
mkdir_p(os.path.dirname(filename))
Expand Down
70 changes: 63 additions & 7 deletions boa/core/recipe_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import sys

from dataclasses import dataclass
from typing import Tuple
from typing import Tuple, List

import rich
from rich.table import Table
Expand Down Expand Up @@ -39,7 +39,7 @@ class CondaBuildSpec:
is_compiler: bool = False
is_transitive_dependency: bool = False
channel: str = ""
# final: String
features: List[str] = None

from_run_export: bool = False
from_pinnings: bool = False
Expand All @@ -48,11 +48,19 @@ def __init__(self, ms):
self.raw = ms
self.splitted = ms.split()
self.name = self.splitted[0]

if len(self.splitted) > 1:
self.is_pin = self.splitted[1].startswith("PIN_")
self.is_pin_compatible = self.splitted[1].startswith("PIN_COMPATIBLE")
self.is_compiler = self.splitted[0].startswith("COMPILER_")

if not (self.is_pin or self.is_pin_compatible or self.is_compiler):
for idx, x in enumerate(self.splitted):
if x.startswith("["):
self.features = [f.strip() for f in x[1:-1].split(",")]
self.splitted = self.splitted[:idx]
break

self.is_simple = len(self.splitted) == 1
self.final = self.raw

Expand All @@ -63,6 +71,17 @@ def __init__(self, ms):
def final_name(self):
return self.final.split(" ")[0]

@property
def final_pin(self):
if hasattr(self, "final_version"):
return f"{self.final_name} {self.final_version[0]} {self.final_version[1]}"
else:
return self.final

@property
def final_triplet(self):
return f"{self.final_name}-{self.final_version[0]}-{self.final_version[1]}"

def loosen_spec(self):
if self.is_compiler or self.is_pin:
return
Expand Down Expand Up @@ -153,6 +172,42 @@ def eval_pin_compatible(self, build, host):
else self.name
)

def eval_features(self, feature_map):
active_features = []
if not self.features:
return

for f in self.features:
if (
f[0] == "&"
and f[1:] in feature_map[f[1:]]
and feature_map[f[1:]]["activated"]
):
active_features.append(f[1:])
elif f[0] != "&":
active_features.append(f)

# special handling with `static` feature
if "static" in active_features:
self.name += "-static"
active_features.remove("static")

if len(active_features):
active_features = sorted(active_features)
feature_string = (
"*" + "".join([f"+{feat}*" for feat in active_features]) + "*"
)
version = "*"
if len(self.splitted) >= 2:
version = self.splitted[1]
else:
feature_string = ""
version = ""
if len(self.splitted) >= 2:
version = self.splitted[1]

self.final = f"{self.name} {version} {feature_string}".strip()


class Output:
def __init__(
Expand Down Expand Up @@ -515,10 +570,9 @@ def propagate_run_exports(self, env, pkg_cache):
continue
if s.name in self.sections["build"].get("ignore_run_exports", []):
continue

if hasattr(s, "final_version"):
final_triple = (
f"{s.final_name}-{s.final_version[0]}-{s.final_version[1]}"
)
final_triplet = s.final_triplet
else:
console.print(f"[red]{s} has no final version")
continue
Expand All @@ -532,7 +586,7 @@ def propagate_run_exports(self, env, pkg_cache):
collected_run_exports.append(s.run_exports_info)
else:
path = Path(pkg_cache).joinpath(
final_triple, "info", "run_exports.json",
final_triplet, "info", "run_exports.json",
)
if path.exists():
with open(path) as fi:
Expand Down Expand Up @@ -582,14 +636,16 @@ def _solve_env(self, env, all_outputs):
if self.requirements.get(env):
console.print(f"Finalizing [yellow]{env}[/yellow] for {self.name}")
specs = self.requirements[env]

for s in specs:
if s.is_pin:
s.eval_pin_subpackage(all_outputs)
if env == "run" and s.is_pin_compatible:
s.eval_pin_compatible(
self.requirements["build"], self.requirements["host"]
)

if s.features:
s.eval_features(self.feature_map)
# save finalized requirements in data for usage in metadata
self.data["requirements"][env] = [s.final for s in self.requirements[env]]

Expand Down
17 changes: 1 addition & 16 deletions boa/core/run_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from boa.core.config import boa_config
from boa.core.validation import validate, ValidationError, SchemaError
from boa.tui.exceptions import BoaRunBuildException
from boa.core.features import extract_features

from conda_build.utils import rm_rf
import conda_build.jinja_context
Expand Down Expand Up @@ -621,22 +622,6 @@ def build_recipe(
return sorted_outputs


def extract_features(feature_string):
if feature_string and len(feature_string):
assert feature_string.startswith("[") and feature_string.endswith("]")
features = [f.strip() for f in feature_string[1:-1].split(",")]
else:
features = []

selected_features = {}
for f in features:
if f.startswith("~"):
selected_features[f[1:]] = False
else:
selected_features[f] = True
return selected_features


def run_build(args):
if getattr(args, "json", False):
global console
Expand Down