Skip to content

Commit 890f000

Browse files
authored
Feature/build order profile args (#17102)
* wip * proposed profile args for build-order * Update conans/client/graph/install_graph.py * review
1 parent 2993d1c commit 890f000

File tree

3 files changed

+70
-12
lines changed

3 files changed

+70
-12
lines changed

conan/cli/commands/graph.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from conan.errors import ConanException
1616
from conan.internal.deploy import do_deploys
1717
from conans.client.graph.graph import BINARY_MISSING
18-
from conans.client.graph.install_graph import InstallGraph
18+
from conans.client.graph.install_graph import InstallGraph, ProfileArgs
1919
from conan.internal.errors import NotFoundException
2020
from conans.model.recipe_ref import ref_matches, RecipeReference
2121

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

120-
install_graph = InstallGraph(deps_graph, order_by=args.order_by)
120+
install_graph = InstallGraph(deps_graph, order_by=args.order_by,
121+
profile_args=ProfileArgs.from_args(args))
121122
if args.reduce:
122123
if args.order_by is None:
123124
raise ConanException("--reduce needs --order-by argument defined")

conans/client/graph/install_graph.py

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -334,17 +334,41 @@ def merge(self, other):
334334
self.filenames.append(d)
335335

336336

337+
class ProfileArgs:
338+
def __init__(self, args):
339+
self._args = args
340+
341+
@staticmethod
342+
def from_args(args):
343+
pr_args = []
344+
for context in "host", "build":
345+
for f in "profile", "settings", "options", "conf":
346+
s = "pr" if f == "profile" else f[0]
347+
pr_args += [f'-{s}:{context[0]}="{v}"' for v in
348+
getattr(args, f"{f}_{context}") or []]
349+
return ProfileArgs(" ".join(pr_args))
350+
351+
@staticmethod
352+
def deserialize(data):
353+
return ProfileArgs(data.get("args"))
354+
355+
def serialize(self):
356+
return {"args": self._args}
357+
358+
337359
class InstallGraph:
338360
""" A graph containing the package references in order to be built/downloaded
339361
"""
340362

341-
def __init__(self, deps_graph, order_by=None):
363+
def __init__(self, deps_graph, order_by=None, profile_args=None):
342364
self._nodes = {} # ref with rev: _InstallGraphNode
343365
order_by = order_by or "recipe"
344366
self._order = order_by
345367
self._node_cls = _InstallRecipeReference if order_by == "recipe" else _InstallConfiguration
346368
self._is_test_package = False
347369
self.reduced = False
370+
self._profiles = {"self": profile_args} if profile_args is not None else {}
371+
self._filename = None
348372
if deps_graph is not None:
349373
self._initialize_deps_graph(deps_graph)
350374
self._is_test_package = deps_graph.root.conanfile.tested_reference_str is not None
@@ -371,15 +395,24 @@ def merge(self, other):
371395
self._nodes[ref] = install_node
372396
else:
373397
existing.merge(install_node)
398+
# Make sure that self is also updated
399+
current = self._profiles.pop("self", None)
400+
if current is not None:
401+
self._profiles[self._filename] = current
402+
new = other._profiles.get("self")
403+
if new is not None:
404+
self._profiles[other._filename] = new
374405

375406
@staticmethod
376407
def deserialize(data, filename):
377408
legacy = isinstance(data, list)
378-
order, data, reduced = ("recipe", data, False) if legacy else \
379-
(data["order_by"], data["order"], data["reduced"])
409+
order, data, reduced, profiles = ("recipe", data, False, {}) if legacy else \
410+
(data["order_by"], data["order"], data["reduced"], data.get("profiles", {}))
380411
result = InstallGraph(None, order_by=order)
381412
result.reduced = reduced
382413
result.legacy = legacy
414+
result._filename = filename
415+
result._profiles = {k: ProfileArgs.deserialize(v) for k, v in profiles.items()}
383416
for level in data:
384417
for item in level:
385418
elem = result._node_cls.deserialize(item, filename)
@@ -464,17 +497,19 @@ def install_build_order(self):
464497
install_order = self.install_order()
465498
result = {"order_by": self._order,
466499
"reduced": self.reduced,
467-
"order": [[n.serialize() for n in level] for level in install_order]}
500+
"order": [[n.serialize() for n in level] for level in install_order],
501+
"profiles": {k: v.serialize() for k, v in self._profiles.items()}
502+
}
468503
return result
469504

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

473-
def analyze_package(package):
474-
if package.binary == BINARY_MISSING:
475-
missing.append(package)
476-
elif package.binary == BINARY_INVALID:
477-
invalid.append(package)
508+
def analyze_package(pkg):
509+
if pkg.binary == BINARY_MISSING:
510+
missing.append(pkg)
511+
elif pkg.binary == BINARY_INVALID:
512+
invalid.append(pkg)
478513
for _, install_node in self._nodes.items():
479514
if self._order == "recipe":
480515
for package in install_node.packages.values():
@@ -508,7 +543,8 @@ def get_errors(self):
508543
return "\n".join(errors)
509544
return None
510545

511-
def _raise_invalid(self, invalid):
546+
@staticmethod
547+
def _raise_invalid(invalid):
512548
msg = ["There are invalid packages:"]
513549
for package in invalid:
514550
node = package.nodes[0]

test/integration/command_v2/test_info_build_order.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,26 @@ def validate(self):
766766
assert "dep/1.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Invalid configuration" in tc.out
767767
assert "IndexError: list index out of range" not in tc.out
768768

769+
770+
def test_multi_configuration_profile_args():
771+
c = TestClient()
772+
c.save({"pkg/conanfile.py": GenConanfile().with_settings("os"),
773+
"consumer/conanfile.txt": "[requires]\npkg/0.1",
774+
"mypr": ""})
775+
c.run("export pkg --name=pkg --version=0.1")
776+
args = "-pr=mypr -s:b os=Linux -o:h *:shared=True -c:h user.my:conf=1"
777+
c.run(f"graph build-order consumer --format=json --build=missing -s os=Windows {args} "
778+
"--order-by=recipe", redirect_stdout="bo_win.json")
779+
c.run(f"graph build-order consumer --format=json --build=missing -s os=Linux {args} "
780+
"--order-by=recipe", redirect_stdout="bo_nix.json")
781+
c.run("graph build-order-merge --file=bo_win.json --file=bo_nix.json --format=json",
782+
redirect_stdout="bo3.json")
783+
bo_json = json.loads(c.load("bo3.json"))
784+
win = '-pr:h="mypr" -s:h="os=Windows" -o:h="*:shared=True" -c:h="user.my:conf=1" -s:b="os=Linux"'
785+
nix = '-pr:h="mypr" -s:h="os=Linux" -o:h="*:shared=True" -c:h="user.my:conf=1" -s:b="os=Linux"'
786+
assert bo_json["profiles"] == {"bo_win": {"args": win}, "bo_nix": {"args": nix}}
787+
788+
769789
def test_build_order_space_in_options():
770790
tc = TestClient(light=True)
771791
tc.save({"dep/conanfile.py": GenConanfile("dep", "1.0")
@@ -784,3 +804,4 @@ def test_build_order_space_in_options():
784804
tc.run("graph build-order . --order-by=configuration --build=dep/1.0 -f=json", redirect_stdout="order.json")
785805
order = json.loads(tc.load("order.json"))
786806
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"'''
807+

0 commit comments

Comments
 (0)