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

Feature/build order profile args #17102

Merged
merged 7 commits into from
Oct 10, 2024
Merged
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
5 changes: 3 additions & 2 deletions conan/cli/commands/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from conan.errors import ConanException
from conan.internal.deploy import do_deploys
from conans.client.graph.graph import BINARY_MISSING
from conans.client.graph.install_graph import InstallGraph
from conans.client.graph.install_graph import InstallGraph, ProfileArgs
from conan.internal.errors import NotFoundException
from conans.model.recipe_ref import ref_matches, RecipeReference

Expand Down Expand Up @@ -117,7 +117,8 @@ def graph_build_order(conan_api, parser, subparser, *args):
out = ConanOutput()
out.title("Computing the build order")

install_graph = InstallGraph(deps_graph, order_by=args.order_by)
install_graph = InstallGraph(deps_graph, order_by=args.order_by,
profile_args=ProfileArgs.from_args(args))
if args.reduce:
if args.order_by is None:
raise ConanException("--reduce needs --order-by argument defined")
Expand Down
56 changes: 46 additions & 10 deletions conans/client/graph/install_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,17 +334,41 @@ def merge(self, other):
self.filenames.append(d)


class ProfileArgs:
def __init__(self, args):
self._args = args

@staticmethod
def from_args(args):
pr_args = []
for context in "host", "build":
for f in "profile", "settings", "options", "conf":
s = "pr" if f == "profile" else f[0]
pr_args += [f'-{s}:{context[0]}="{v}"' for v in
getattr(args, f"{f}_{context}") or []]
return ProfileArgs(" ".join(pr_args))

@staticmethod
def deserialize(data):
return ProfileArgs(data.get("args"))

def serialize(self):
return {"args": self._args}


class InstallGraph:
""" A graph containing the package references in order to be built/downloaded
"""

def __init__(self, deps_graph, order_by=None):
def __init__(self, deps_graph, order_by=None, profile_args=None):
self._nodes = {} # ref with rev: _InstallGraphNode
order_by = order_by or "recipe"
self._order = order_by
self._node_cls = _InstallRecipeReference if order_by == "recipe" else _InstallConfiguration
self._is_test_package = False
self.reduced = False
self._profiles = {"self": profile_args} if profile_args is not None else {}
self._filename = None
if deps_graph is not None:
self._initialize_deps_graph(deps_graph)
self._is_test_package = deps_graph.root.conanfile.tested_reference_str is not None
Expand All @@ -371,15 +395,24 @@ def merge(self, other):
self._nodes[ref] = install_node
else:
existing.merge(install_node)
# Make sure that self is also updated
current = self._profiles.pop("self", None)
if current is not None:
self._profiles[self._filename] = current
new = other._profiles.get("self")
if new is not None:
self._profiles[other._filename] = new

@staticmethod
def deserialize(data, filename):
legacy = isinstance(data, list)
order, data, reduced = ("recipe", data, False) if legacy else \
(data["order_by"], data["order"], data["reduced"])
order, data, reduced, profiles = ("recipe", data, False, {}) if legacy else \
(data["order_by"], data["order"], data["reduced"], data.get("profiles", {}))
result = InstallGraph(None, order_by=order)
result.reduced = reduced
result.legacy = legacy
result._filename = filename
result._profiles = {k: ProfileArgs.deserialize(v) for k, v in profiles.items()}
for level in data:
for item in level:
elem = result._node_cls.deserialize(item, filename)
Expand Down Expand Up @@ -464,17 +497,19 @@ def install_build_order(self):
install_order = self.install_order()
result = {"order_by": self._order,
"reduced": self.reduced,
"order": [[n.serialize() for n in level] for level in install_order]}
"order": [[n.serialize() for n in level] for level in install_order],
"profiles": {k: v.serialize() for k, v in self._profiles.items()}
}
return result

def _get_missing_invalid_packages(self):
missing, invalid = [], []

def analyze_package(package):
if package.binary == BINARY_MISSING:
missing.append(package)
elif package.binary == BINARY_INVALID:
invalid.append(package)
def analyze_package(pkg):
if pkg.binary == BINARY_MISSING:
missing.append(pkg)
elif pkg.binary == BINARY_INVALID:
invalid.append(pkg)
for _, install_node in self._nodes.items():
if self._order == "recipe":
for package in install_node.packages.values():
Expand Down Expand Up @@ -508,7 +543,8 @@ def get_errors(self):
return "\n".join(errors)
return None

def _raise_invalid(self, invalid):
@staticmethod
def _raise_invalid(invalid):
msg = ["There are invalid packages:"]
for package in invalid:
node = package.nodes[0]
Expand Down
21 changes: 21 additions & 0 deletions test/integration/command_v2/test_info_build_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,26 @@ def validate(self):
assert "dep/1.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Invalid configuration" in tc.out
assert "IndexError: list index out of range" not in tc.out


def test_multi_configuration_profile_args():
c = TestClient()
c.save({"pkg/conanfile.py": GenConanfile().with_settings("os"),
"consumer/conanfile.txt": "[requires]\npkg/0.1",
"mypr": ""})
c.run("export pkg --name=pkg --version=0.1")
args = "-pr=mypr -s:b os=Linux -o:h *:shared=True -c:h user.my:conf=1"
c.run(f"graph build-order consumer --format=json --build=missing -s os=Windows {args} "
"--order-by=recipe", redirect_stdout="bo_win.json")
c.run(f"graph build-order consumer --format=json --build=missing -s os=Linux {args} "
"--order-by=recipe", redirect_stdout="bo_nix.json")
c.run("graph build-order-merge --file=bo_win.json --file=bo_nix.json --format=json",
redirect_stdout="bo3.json")
bo_json = json.loads(c.load("bo3.json"))
win = '-pr:h="mypr" -s:h="os=Windows" -o:h="*:shared=True" -c:h="user.my:conf=1" -s:b="os=Linux"'
nix = '-pr:h="mypr" -s:h="os=Linux" -o:h="*:shared=True" -c:h="user.my:conf=1" -s:b="os=Linux"'
assert bo_json["profiles"] == {"bo_win": {"args": win}, "bo_nix": {"args": nix}}


def test_build_order_space_in_options():
tc = TestClient(light=True)
tc.save({"dep/conanfile.py": GenConanfile("dep", "1.0")
Expand All @@ -784,3 +804,4 @@ def test_build_order_space_in_options():
tc.run("graph build-order . --order-by=configuration --build=dep/1.0 -f=json", redirect_stdout="order.json")
order = json.loads(tc.load("order.json"))
assert order["order"][0][0]["build_args"] == '''--requires=dep/1.0 --build=dep/1.0 -o="dep/*:extras=cxx="yes" gnuext='no'" -o="dep/*:flags=define=FOO define=BAR define=BAZ"'''