Repository: conan-io/conan
Pull request #16594 (2024-07-03 10:11:33)

Quote build_args in conan graph build-order
Changelog: Fix: Quote `build_args` in `conan graph build-order -f=json` to avoid issues with options with spaces
Changelog: Bugfix: `build_args` options in `graph build-order` now respect the context of the reference
Docs: Omit



Closes https://github.com/conan-io/conan/issues/16593



Patch:
diff --git a/.github/stale.yml b/.github/stale.yml
deleted file mode 100644
index 959462acf..000000000
--- a/.github/stale.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-# Configuration for probot-stale - https://github.com/probot/stale
-
-# Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 60
-
-# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
-# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 7
-
-# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
-onlyLabels:
-    - "type: question"
-
-# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
-exemptLabels:
-    - "stage: triaging"
-
-# Set to true to ignore issues in a project (defaults to false)
-exemptProjects: false
-
-# Set to true to ignore issues in a milestone (defaults to false)
-exemptMilestones: false
-
-# Set to true to ignore issues with an assignee (defaults to false)
-exemptAssignees: false
-
-# Label to use when marking as stale
-staleLabel: staled
-
-# Comment to post when marking as stale. Set to `false` to disable
-markComment: >
-  This issue has been automatically marked as stale because it has not had
-  recent activity. It will be closed if no further activity occurs. Thank you
-  for your contributions.
-
-# Comment to post when removing the stale label.
-# unmarkComment: >
-#   Your comment here.
-
-# Comment to post when closing a stale Issue or Pull Request.
- closeComment: >
-   This issue has been automatically closed because it has not had recent activity. Feel
-     free to reopen it if necessary. Thank you for your contributions.
-
-# Limit the number of actions per hour, from 1-30. Default is 30
-limitPerRun: 30
-
-# Limit to only `issues` or `pulls`
-only: issues
-
-# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
-# pulls:
-#   daysUntilStale: 30
-#   markComment: >
-#     This pull request has been automatically marked as stale because it has not had
-#     recent activity. It will be closed if no further activity occurs. Thank you
-#     for your contributions.
-
-# issues:
-#   exemptLabels:
-#     - confirmed
diff --git a/conan/cli/commands/graph.py b/conan/cli/commands/graph.py
index ee0c4164b..60e71f0d0 100644
--- a/conan/cli/commands/graph.py
+++ b/conan/cli/commands/graph.py
@@ -10,6 +10,7 @@ from conan.cli.commands.list import prepare_pkglist_compact, print_serial
 from conan.cli.formatters.graph import format_graph_html, format_graph_json, format_graph_dot
 from conan.cli.formatters.graph.build_order_html import format_build_order_html
 from conan.cli.formatters.graph.graph_info_text import format_graph_info
+from conan.cli.printers import print_profiles
 from conan.cli.printers.graph import print_graph_packages, print_graph_basic
 from conan.errors import ConanException
 from conan.internal.deploy import do_deploys
@@ -209,6 +210,7 @@ def graph_info(conan_api, parser, subparser, *args):
                                                partial=args.lockfile_partial,
                                                overrides=overrides)
     profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)
+    print_profiles(profile_host, profile_build)
 
     if path:
         deps_graph = conan_api.graph.load_graph_consumer(path, args.name, args.version,
diff --git a/conans/client/graph/install_graph.py b/conans/client/graph/install_graph.py
index d51f9082c..cc7db6587 100644
--- a/conans/client/graph/install_graph.py
+++ b/conans/client/graph/install_graph.py
@@ -1,5 +1,6 @@
 import json
 import os
+import shlex
 import textwrap
 
 from conan.api.output import ConanOutput
@@ -72,7 +73,8 @@ class _InstallPackageReference:
         compatible = "compatible:" if self.info and self.info.get("compatibility_delta") else ""
         cmd += f" --build={compatible}{self.ref}"
         if self.options:
-            cmd += " " + " ".join(f"-o {o}" for o in self.options)
+            scope = "" if self.context == "host" else ":b"
+            cmd += " " + " ".join(f'-o{scope}="{o}"' for o in self.options)
         if self.overrides:
             cmd += f' --lockfile-overrides="{self.overrides}"'
         return cmd
@@ -283,7 +285,8 @@ class _InstallConfiguration:
         compatible = "compatible:" if self.info and self.info.get("compatibility_delta") else ""
         cmd += f" --build={compatible}{self.ref}"
         if self.options:
-            cmd += " " + " ".join(f"-o {o}" for o in self.options)
+            scope = "" if self.context == "host" else ":b"
+            cmd += " " + " ".join(f'-o{scope}="{o}"' for o in self.options)
         if self.overrides:
             cmd += f' --lockfile-overrides="{self.overrides}"'
         return cmd


Test patch:
diff --git a/test/integration/command_v2/test_info_build_order.py b/test/integration/command_v2/test_info_build_order.py
index 21f6b86a4..bb9c688c7 100644
--- a/test/integration/command_v2/test_info_build_order.py
+++ b/test/integration/command_v2/test_info_build_order.py
@@ -237,13 +237,13 @@ def test_info_build_order_options():
                   'context': 'build', 'depends': [], "overrides": {},
                   'binary': 'Build', 'options': ['tool/0.1:myopt=2'], 'filenames': [],
                   'info': {'options': {'myopt': '2'}},
-                  'build_args': '--tool-requires=tool/0.1 --build=tool/0.1 -o tool/0.1:myopt=2'},
+                  'build_args': '--tool-requires=tool/0.1 --build=tool/0.1 -o:b="tool/0.1:myopt=2"'},
                  {'package_id': 'a9035d84c5880b26c4b44acf70078c9a7dd37412', 'prev': None,
                   'context': 'build', 'depends': [], "overrides": {},
                   'info': {'options': {'myopt': '1'}},
                   'binary': 'Build', 'options': ['tool/0.1:myopt=1'],
                   'filenames': [],
-                  'build_args': '--tool-requires=tool/0.1 --build=tool/0.1 -o tool/0.1:myopt=1'}
+                  'build_args': '--tool-requires=tool/0.1 --build=tool/0.1 -o:b="tool/0.1:myopt=1"'}
              ]]}
         ],
         [
@@ -765,3 +765,22 @@ class TestBuildOrderReduce:
         tc.run("graph build-order-merge --file=order.json --file=order.json --format=json", assert_error=True)
         assert "dep/1.0:da39a3ee5e6b4b0d3255bfef95601890afd80709: Invalid configuration" in tc.out
         assert "IndexError: list index out of range" not in tc.out
+
+def test_build_order_space_in_options():
+    tc = TestClient(light=True)
+    tc.save({"dep/conanfile.py": GenConanfile("dep", "1.0")
+                .with_option("flags", ["ANY", None])
+                .with_option("extras", ["ANY", None]),
+             "conanfile.txt": textwrap.dedent("""
+             [requires]
+             dep/1.0
+
+             [options]
+             dep/*:flags=define=FOO define=BAR define=BAZ
+             dep/*:extras=cxx="yes" gnuext='no'
+             """)})
+
+    tc.run("create dep")
+    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"'''