From 45ca72bb72c330c3f26eb1b0fb106061140fc703 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 22 Jan 2019 12:25:23 +0100 Subject: [PATCH 01/11] add json output to info command (both console and file) --- conans/client/command.py | 16 +- conans/client/conan_command_output.py | 135 +++++++++++++- conans/client/printer.py | 173 ++++++------------ .../functional/command/info_command_test.py | 86 +++++---- conans/test/functional/command/info_test.py | 24 ++- 5 files changed, 274 insertions(+), 160 deletions(-) diff --git a/conans/client/command.py b/conans/client/command.py index d7cf2b53358..c292493f10b 100644 --- a/conans/client/command.py +++ b/conans/client/command.py @@ -475,8 +475,7 @@ def info(self, *args): "specify both install-folder and any setting/option " "it will raise an error.") parser.add_argument("-j", "--json", nargs='?', const="1", type=str, - help='Only with --build-order option, return the information in a json.' - ' e.g --json=/path/to/filename.json or --json to output the json') + help='Path to a json file where the information will be written') parser.add_argument("-n", "--only", nargs=1, action=Extender, help="Show only the specified fields: %s. '--paths' information can " "also be filtered with options %s. Use '--only None' to show only " @@ -485,8 +484,8 @@ def info(self, *args): help='Print information only for packages that match the filter pattern' ' e.g., MyPackage/1.2@user/channel or MyPackage*') - dry_build_help = ("Apply the --build argument to output the information, as it would be done " - "by the install command") + dry_build_help = ("Apply the --build argument to output the information, as it would be done" + " by the install command") parser.add_argument("-db", "--dry-build", action=Extender, nargs="?", help=dry_build_help) build_help = ("Given a build policy, return an ordered list of packages that would be built" " from sources during the install command") @@ -526,7 +525,11 @@ def info(self, *args): remote_name=args.remote, check_updates=args.update, install_folder=args.install_folder) - self._outputer.nodes_to_build(nodes) + if args.json: + json_arg = True if args.json == "1" else args.json + self._outputer.json_nodes_to_build(nodes, json_arg, get_cwd()) + else: + self._outputer.nodes_to_build(nodes) # INFO ABOUT DEPS OF CURRENT PROJECT OR REFERENCE else: @@ -553,6 +556,9 @@ def info(self, *args): if args.graph: self._outputer.info_graph(args.graph, deps_graph, get_cwd()) + elif args.json: + json_arg = True if args.json == "1" else args.json + self._outputer.json_info(deps_graph, json_arg, get_cwd()) else: self._outputer.info(deps_graph, only, args.package_filter, args.paths) diff --git a/conans/client/conan_command_output.py b/conans/client/conan_command_output.py index 7c7744f0e95..1fcbcb206a0 100644 --- a/conans/client/conan_command_output.py +++ b/conans/client/conan_command_output.py @@ -1,13 +1,17 @@ import json import os +from collections import OrderedDict from conans.client.graph.graph import RECIPE_CONSUMER, RECIPE_VIRTUAL +from conans.client.graph.graph import RECIPE_EDITABLE +from conans.client.installer import build_id from conans.client.printer import Printer from conans.model.ref import ConanFileReference, PackageReference +from conans.paths.simple_paths import SimplePaths from conans.search.binary_html_table import html_binary_graph from conans.unicode import get_cwd +from conans.util.env_reader import get_env from conans.util.files import save -from conans.client.graph.graph import RECIPE_EDITABLE class CommandOutputer(object): @@ -82,13 +86,117 @@ def _read_dates(self, deps_graph): def nodes_to_build(self, nodes_to_build): self.user_io.out.info(", ".join(str(n) for n in nodes_to_build)) + def json_nodes_to_build(self, nodes_to_build, json_output, cwd): + data = [str(n) for n in nodes_to_build] + json_str = json.dumps(data) + + if json_output is True: + self.user_io.out.write(json_str) + else: + if not os.path.isabs(json_output): + json_output = os.path.join(cwd, json_output) + save(json_output, json.dumps(data)) + self.user_io.out.writeln("") + self.user_io.out.info("JSON file created at '%s'" % json_output) + + def _grab_info_data(self, deps_graph): + """ Convert 'deps_graph' into consumible information for json and cli """ + compact_nodes = OrderedDict() + for node in sorted(deps_graph.nodes): + compact_nodes.setdefault((node.ref, node.conanfile.info.package_id()), []).append(node) + + ret = [] + for (ref, package_id), list_nodes in compact_nodes.items(): + node = list_nodes[0] + if node.recipe == RECIPE_VIRTUAL: + continue + + item_data = {} + conanfile = node.conanfile + if node.recipe == RECIPE_CONSUMER: + ref = str(conanfile) + + item_data["reference"] = str(ref) + item_data["is_ref"] = isinstance(ref, ConanFileReference) + item_data["display_name"] = conanfile.display_name + item_data["id"] = package_id + item_data["build_id"] = build_id(conanfile) + + # Paths + if isinstance(ref, ConanFileReference): + item_data["export_folder"] = self.cache.export(ref) + item_data["source_folder"] = self.cache.source(ref, conanfile.short_paths) + if isinstance(self.cache, SimplePaths): + # @todo: check if this is correct or if it must always be package_id() + bid = build_id(conanfile) + if not bid: + bid = conanfile.info.package_id() + pref = PackageReference(ref, bid) + item_data["build_folder"] = self.cache.build(pref, conanfile.short_paths) + + package_id = conanfile.info.package_id() + pref = PackageReference(ref, package_id) + item_data["package_folder"] = self.cache.package(pref, conanfile.short_paths) + + try: + reg_remote = self.cache.registry.refs.get(ref) + if reg_remote: + item_data["remote"] = {"name": reg_remote.name, "url": reg_remote.url} + except: + pass + + def _add_if_exists(attrib, as_list=False): + value = getattr(conanfile, attrib, None) + if value: + if not as_list: + item_data[attrib] = value + else: + item_data[attrib] = list(value) if isinstance(value, (list, tuple, set)) \ + else [value, ] + + _add_if_exists("url") + _add_if_exists("homepage") + _add_if_exists("license", as_list=True) + _add_if_exists("author") + _add_if_exists("topics", as_list=True) + + if isinstance(ref, ConanFileReference): + item_data["recipe"] = node.recipe + + if get_env("CONAN_CLIENT_REVISIONS_ENABLED", False) and node.ref.revision: + item_data["revision"] = node.ref.revision + + item_data["binary"] = node.binary + if node.binary_remote: + item_data["binary_remote"] = node.binary_remote.name + + node_times = self._read_dates(deps_graph) + if node_times and node_times.get(ref, None): + item_data["creation_date"] = node_times.get(ref, None) + + if isinstance(ref, ConanFileReference): + dependants = [n for node in list_nodes for n in node.inverse_neighbors()] + required = [d.conanfile for d in dependants if d.recipe != RECIPE_VIRTUAL] + if required: + item_data["required_by"] = [d.display_name for d in required] + + depends = node.neighbors() + requires = [d for d in depends if not d.build_require] + build_requires = [d for d in depends if d.build_require] + + if requires: + item_data["requires"] = [repr(d.ref) for d in requires] + + if build_requires: + item_data["build_requires"] = [repr(d.ref) for d in build_requires] + + ret.append(item_data) + + return ret + def info(self, deps_graph, only, package_filter, show_paths): - registry = self.cache.registry - Printer(self.user_io.out).print_info(deps_graph, - only, registry, - node_times=self._read_dates(deps_graph), - path_resolver=self.cache, - package_filter=package_filter, + data = self._grab_info_data(deps_graph) + Printer(self.user_io.out).print_info(data, only, package_filter=package_filter, show_paths=show_paths) def info_graph(self, graph_filename, deps_graph, cwd): @@ -104,6 +212,19 @@ def info_graph(self, graph_filename, deps_graph, cwd): graph_filename = os.path.join(cwd, graph_filename) grapher.graph_file(graph_filename) + def json_info(self, deps_graph, json_output, cwd): + data = self._grab_info_data(deps_graph) + json_str = json.dumps(data) + + if json_output is True: + self.user_io.out.write(json_str) + else: + if not os.path.isabs(json_output): + json_output = os.path.join(cwd, json_output) + save(json_output, json.dumps(data)) + self.user_io.out.writeln("") + self.user_io.out.info("JSON file created at '%s'" % json_output) + def print_search_references(self, search_info, pattern, raw, all_remotes_search): printer = Printer(self.user_io.out) printer.print_search_recipes(search_info, pattern, raw, all_remotes_search) diff --git a/conans/client/printer.py b/conans/client/printer.py index b91458d9452..7d4347bab7c 100644 --- a/conans/client/printer.py +++ b/conans/client/printer.py @@ -1,13 +1,9 @@ import fnmatch from collections import OrderedDict -from conans.client.installer import build_id from conans.client.output import Color from conans.model.options import OptionsValues -from conans.model.ref import ConanFileReference, PackageReference -from conans.paths.simple_paths import SimplePaths -from conans.util.env_reader import get_env -from conans.client.graph.graph import RECIPE_CONSUMER, RECIPE_VIRTUAL +from conans.model.ref import ConanFileReference class Printer(object): @@ -40,36 +36,8 @@ def print_inspect(self, inspect): else: self._out.writeln("%s: %s" % (k, str(v))) - def _print_paths(self, ref, conan, path_resolver, show): - if isinstance(ref, ConanFileReference): - if show("export_folder"): - path = path_resolver.export(ref) - self._out.writeln(" export_folder: %s" % path, Color.BRIGHT_GREEN) - if show("source_folder"): - path = path_resolver.source(ref, conan.short_paths) - self._out.writeln(" source_folder: %s" % path, Color.BRIGHT_GREEN) - if show("build_folder") and isinstance(path_resolver, SimplePaths): - # @todo: check if this is correct or if it must always be package_id() - bid = build_id(conan) - if not bid: - bid = conan.info.package_id() - path = path_resolver.build(PackageReference(ref, bid), conan.short_paths) - self._out.writeln(" build_folder: %s" % path, Color.BRIGHT_GREEN) - if show("package_folder") and isinstance(path_resolver, SimplePaths): - id_ = conan.info.package_id() - path = path_resolver.package(PackageReference(ref, id_), conan.short_paths) - self._out.writeln(" package_folder: %s" % path, Color.BRIGHT_GREEN) - - def print_info(self, deps_graph, _info, registry, node_times=None, path_resolver=None, - package_filter=None, show_paths=False): - """ Print the dependency information for a conan file - - Attributes: - deps_graph: the dependency graph of conan file references to print - placeholder_reference: the conan file reference that represents the conan - file for a project on the path. This may be None, - in which case the project itself will not be part - of the printed dependencies. + def print_info(self, data, _info, package_filter=None, show_paths=False): + """ Print in console the dependency information for a conan file """ if _info is None: # No filter def show(_): @@ -80,102 +48,81 @@ def show(_): def show(field): return field in _info_lower - compact_nodes = OrderedDict() - for node in sorted(deps_graph.nodes): - compact_nodes.setdefault((node.ref, node.conanfile.info.package_id()), []).append(node) - - for (ref, package_id), list_nodes in compact_nodes.items(): - node = list_nodes[0] - conan = node.conanfile - if node.recipe == RECIPE_VIRTUAL: - continue - if node.recipe == RECIPE_CONSUMER: - ref = str(conan) - if package_filter and not fnmatch.fnmatch(str(ref), package_filter): + for it in data: + if package_filter and not fnmatch.fnmatch(it["reference"], package_filter): continue - self._out.writeln("%s" % node.conanfile.display_name, Color.BRIGHT_CYAN) - try: - # Excludes PROJECT fake reference - reg_remote = registry.refs.get(ref) - except: - reg_remote = None + is_ref = it["is_ref"] + self._out.writeln(it["display_name"], Color.BRIGHT_CYAN) if show("id"): - self._out.writeln(" ID: %s" % package_id, Color.BRIGHT_GREEN) + self._out.writeln(" ID: %s" % it["id"], Color.BRIGHT_GREEN) if show("build_id"): - bid = build_id(conan) - self._out.writeln(" BuildID: %s" % bid, Color.BRIGHT_GREEN) - + self._out.writeln(" BuildID: %s" % it["build_id"], Color.BRIGHT_GREEN) if show_paths: - self._print_paths(ref, conan, path_resolver, show) + if show("export_folder"): + self._out.writeln(" export_folder: %s" % it["export_folder"], + Color.BRIGHT_GREEN) + if show("source_folder"): + self._out.writeln(" source_folder: %s" % it["source_folder"], + Color.BRIGHT_GREEN) + if show("build_folder") and "build_folder" in it: + self._out.writeln(" build_folder: %s" % it["build_folder"], + Color.BRIGHT_GREEN) + if show("package_folder") and "package_folder" in it: + self._out.writeln(" package_folder: %s" % it["package_folder"], + Color.BRIGHT_GREEN) - if isinstance(ref, ConanFileReference) and show("remote"): - if reg_remote: - self._out.writeln(" Remote: %s=%s" % (reg_remote.name, reg_remote.url), + if show("remote") and is_ref: + if "remote" in it: + self._out.writeln(" Remote: %s=%s" % (it["remote"]["name"], + it["remote"]["url"]), Color.BRIGHT_GREEN) else: self._out.writeln(" Remote: None", Color.BRIGHT_GREEN) - url = getattr(conan, "url", None) - homepage = getattr(conan, "homepage", None) - license_ = getattr(conan, "license", None) - author = getattr(conan, "author", None) - topics = getattr(conan, "topics", None) - if url and show("url"): - self._out.writeln(" URL: %s" % url, Color.BRIGHT_GREEN) - if homepage and show("homepage"): - self._out.writeln(" Homepage: %s" % homepage, Color.BRIGHT_GREEN) - if license_ and show("license"): - if isinstance(license_, (list, tuple, set)): - self._out.writeln(" Licenses: %s" % ", ".join(license_), Color.BRIGHT_GREEN) + if show("url") and "url" in it: + self._out.writeln(" URL: %s" % it["url"], Color.BRIGHT_GREEN) + if show("homepage") and "homepage" in it: + self._out.writeln(" Homepage: %s" % it["homepage"], Color.BRIGHT_GREEN) + if show("license") and "license" in it: + licenses_str = ", ".join(it["license"]) + lead_str = "Licenses" if len(it["license"]) > 1 else "License" + self._out.writeln(" %s: %s" % (lead_str, licenses_str), Color.BRIGHT_GREEN) + if show("author") and "author" in it: + self._out.writeln(" Author: %s" % it["author"], Color.BRIGHT_GREEN) + if show("topics") and "topics" in it: + self._out.writeln(" Topics: %s" % ", ".join(it["topics"]), Color.BRIGHT_GREEN) + if show("recipe") and "recipe" in it: + self._out.writeln(" Recipe: %s" % it["recipe"]) + if show("revision") and "revision" in it: + self._out.writeln(" Revision: %s" % it["revision"]) + if show("binary") and "binary" in it: + self._out.writeln(" Binary: %s" % it["binary"]) + if show("binary_remote") and is_ref: + if "binary_remote" in it: + self._out.writeln(" Binary remote: %s" % it["binary_remote"]) else: - self._out.writeln(" License: %s" % license_, Color.BRIGHT_GREEN) - if author and show("author"): - self._out.writeln(" Author: %s" % author, Color.BRIGHT_GREEN) - if topics and show("topics"): - if isinstance(topics, (list, tuple, set)): - self._out.writeln(" Topics: %s" % ", ".join(topics), Color.BRIGHT_GREEN) - else: - self._out.writeln(" Topics: %s" % topics, Color.BRIGHT_GREEN) - - if isinstance(ref, ConanFileReference) and show("recipe"): # Excludes PROJECT - self._out.writeln(" Recipe: %s" % node.recipe) - revisions_enabled = get_env("CONAN_CLIENT_REVISIONS_ENABLED", False) - if revisions_enabled: - if (isinstance(ref, ConanFileReference) and show("revision") and - node.ref.revision): - self._out.writeln(" Revision: %s" % node.ref.revision) - if isinstance(ref, ConanFileReference) and show("binary"): # Excludes PROJECT - self._out.writeln(" Binary: %s" % node.binary) - if isinstance(ref, ConanFileReference) and show("binary_remote"): # Excludes PROJECT - self._out.writeln(" Binary remote: %s" % (node.binary_remote.name - if node.binary_remote else "None")) + self._out.writeln(" Binary remote: None") - if node_times and node_times.get(ref, None) and show("date"): - self._out.writeln(" Creation date: %s" % node_times.get(ref, None), - Color.BRIGHT_GREEN) + if show("date") and "creation_date" in it: + self._out.writeln(" Creation date: %s" % it["creation_date"], Color.BRIGHT_GREEN) - dependants = [n for node in list_nodes for n in node.inverse_neighbors()] - if isinstance(ref, ConanFileReference) and show("required"): # Excludes - required = [d.conanfile for d in dependants if d.recipe != RECIPE_VIRTUAL] - if required: - self._out.writeln(" Required by:", Color.BRIGHT_GREEN) - for d in required: - self._out.writeln(" %s" % d.display_name, Color.BRIGHT_YELLOW) + if show("required") and "required_by" in it: + self._out.writeln(" Required by:", Color.BRIGHT_GREEN) + for d in it["required_by"]: + self._out.writeln(" %s" % d, Color.BRIGHT_YELLOW) if show("requires"): - depends = node.neighbors() - requires = [d for d in depends if not d.build_require] - build_requires = [d for d in depends if d.build_require] - if requires: + if "requires" in it: self._out.writeln(" Requires:", Color.BRIGHT_GREEN) - for d in requires: - self._out.writeln(" %s" % repr(d.ref), Color.BRIGHT_YELLOW) - if build_requires: + for d in it["requires"]: + self._out.writeln(" %s" % d, Color.BRIGHT_YELLOW) + + if "build_requires" in it: self._out.writeln(" Build Requires:", Color.BRIGHT_GREEN) - for d in build_requires: - self._out.writeln(" %s" % repr(d.ref), Color.BRIGHT_YELLOW) + for d in it["build_requires"]: + self._out.writeln(" %s" % d, Color.BRIGHT_YELLOW) def print_search_recipes(self, search_info, pattern, raw, all_remotes_search): """ Print all the exported conans information diff --git a/conans/test/functional/command/info_command_test.py b/conans/test/functional/command/info_command_test.py index 2bc228bafaf..22da91958a4 100644 --- a/conans/test/functional/command/info_command_test.py +++ b/conans/test/functional/command/info_command_test.py @@ -3,6 +3,7 @@ from conans.paths import CONANFILE from conans.test.utils.cpp_test_files import cpp_hello_conan_files +from conans.test.utils.test_files import temp_folder from conans.test.utils.tools import TestClient, TestServer from conans.util.files import load, save @@ -14,40 +15,26 @@ def setUp(self): self.servers = {"default": test_server} self.clients = {} - def _export(self, name=0, version=None, deps=None): - client = TestClient(servers=self.servers, users={"default": [("lu", "mypass")]}) - self.clients[name] = client - # Not necessary to actually build binaries - files = cpp_hello_conan_files(name, version, deps, build=False) - client.save(files, clean_first=True) - client.run("export . lu/st") - client.run("upload %s/%s@lu/st" % (name, version)) - - def assert_last_line(self, client, line): - lastline = str(client.user_io.out).splitlines()[-1] - self.assertEquals(lastline, line) - - def info_build_test(self): - """Test that the output of 'conan info --build' is correct - - +-----------+ - +------> | H0 | <--------+ - | +------+----+ | - private | ^ |private - | | | - +----+-----+ +----+------+ +----+------+ - | H1a | | H1b | | H1c | - +----+-----+ +-----------+ +----+------+ - ^ ^ - | | - | | -+----------+-+ +-------+------+ -| H2a | <------+ +-----> | H2c | -+------------+ | | +--------------+ - | | - +---+----+---+ - | H3 | - +------------+ + # Build a complex graph + """ + +-----------+ + +------> | H0 | <--------+ + | +------+----+ | + private | ^ |private + | | | + +----+-----+ +----+------+ +----+------+ + | H1a | | H1b | | H1c | + +----+-----+ +-----------+ +----+------+ + ^ ^ + | | + | | + +----------+-+ +-------+------+ + | H2a | <------+ +-----> | H2c | + +------------+ | | +--------------+ + | | + +---+----+---+ + | H3 | + +------------+ """ @@ -63,6 +50,21 @@ def info_build_test(self): self._export("H3", "0.1", deps=["H2a/0.1@lu/st", "H2c/0.1@lu/st"]) + def _export(self, name=0, version=None, deps=None): + client = TestClient(servers=self.servers, users={"default": [("lu", "mypass")]}) + self.clients[name] = client + # Not necessary to actually build binaries + files = cpp_hello_conan_files(name, version, deps, build=False) + client.save(files, clean_first=True) + client.run("export . lu/st") + client.run("upload %s/%s@lu/st" % (name, version)) + + def assert_last_line(self, client, line): + lastline = str(client.user_io.out).splitlines()[-1] + self.assertEquals(lastline, line) + + def info_build_test(self): + """ Test that the output of 'conan info --build' is correct """ # If we install H3 we need to build all except H1b self.clients["H3"].run("info . --build missing") self.assert_last_line(self.clients["H3"], @@ -118,3 +120,19 @@ def info_build_test(self): self.clients["H3"].run("info . --build outdated") self.assert_last_line(self.clients["H3"], "H0/0.1@lu/st, H1a/0.1@lu/st, H2a/0.1@lu/st, H2c/0.1@lu/st") + + def test_json_output_build(self): + """ Test that the output of 'conan info --build' to json file is correct """ + json_file = os.path.join(temp_folder(), "output.json") + json_output = '["H0/0.1@lu/st", "H1a/0.1@lu/st", "H1c/0.1@lu/st", ' \ + '"H2a/0.1@lu/st", "H2c/0.1@lu/st"]' + + self.clients["H3"].run("info . --build missing --json=\"{}\"".format(json_file)) + self.assertEqual(load(json_file), json_output) + + json_file = os.path.join(temp_folder(), "output.json") + self.clients["H3"].run("info . --build missing --json".format(json_file)) + self.assert_last_line(self.clients["H3"], json_output) + + + diff --git a/conans/test/functional/command/info_test.py b/conans/test/functional/command/info_test.py index 3e8d67a2679..dec1eef2919 100644 --- a/conans/test/functional/command/info_test.py +++ b/conans/test/functional/command/info_test.py @@ -1,3 +1,4 @@ +import json import os import re import textwrap @@ -296,7 +297,7 @@ def info_virtual_test(self): self.assertNotIn("virtual", self.client.out) self.assertNotIn("Required", self.client.out) - def reuse_test(self): + def test_reuse_test(self): self.client = TestClient() self._create("Hello0", "0.1") self._create("Hello1", "0.1", ["Hello0/0.1@lasote/stable"]) @@ -383,6 +384,27 @@ def clean_output(output): self.assertIn(expected_output, clean_output(self.client.user_io.out)) + def test_json_info_outpus(self): + self.client = TestClient() + self._create("LibA", "0.1") + self._create("LibE", "0.1") + self._create("LibF", "0.1") + + self._create("LibB", "0.1", ["LibA/0.1@lasote/stable", "LibE/0.1@lasote/stable"]) + self._create("LibC", "0.1", ["LibA/0.1@lasote/stable", "LibF/0.1@lasote/stable"]) + + self._create("LibD", "0.1", ["LibB/0.1@lasote/stable", "LibC/0.1@lasote/stable"], + export=False) + + json_file = os.path.join(self.client.current_folder, "output.json") + self.client.run("info . -u --json=\"{}\"".format(json_file)) + + # Check a couple of values in the generated JSON + content = json.loads(load(json_file)) + self.assertEqual(content[0]["reference"], "LibA/0.1@lasote/stable") + self.assertEqual(content[0]["license"][0], "MIT") + self.assertEqual(content[1]["id"], "c4ec2bf350e2a02405029ab366535e26372a4f63") + def build_order_test(self): self.client = TestClient() self._create("Hello0", "0.1") From 8af3862ca02e67842ee91c25a9113a694f3b9ede Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 22 Jan 2019 12:40:28 +0100 Subject: [PATCH 02/11] add handy function --- conans/client/printer.py | 56 +++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/conans/client/printer.py b/conans/client/printer.py index 7d4347bab7c..e1d1f8329a8 100644 --- a/conans/client/printer.py +++ b/conans/client/printer.py @@ -48,6 +48,14 @@ def show(_): def show(field): return field in _info_lower + # Handy function to perform a common print task + def _print(it_field, show_field=None, name=None, color=Color.BRIGHT_GREEN): + show_field = show_field or it_field + name = name or it_field + if show(show_field) and it_field in it: + self._out.writeln(" %s: %s" % (name, it[it_field]), color) + + # Iteration and printing for it in data: if package_filter and not fnmatch.fnmatch(it["reference"], package_filter): continue @@ -55,23 +63,13 @@ def show(field): is_ref = it["is_ref"] self._out.writeln(it["display_name"], Color.BRIGHT_CYAN) - if show("id"): - self._out.writeln(" ID: %s" % it["id"], Color.BRIGHT_GREEN) - if show("build_id"): - self._out.writeln(" BuildID: %s" % it["build_id"], Color.BRIGHT_GREEN) + _print("id", name="ID") + _print("build_id", name="BuildID") if show_paths: - if show("export_folder"): - self._out.writeln(" export_folder: %s" % it["export_folder"], - Color.BRIGHT_GREEN) - if show("source_folder"): - self._out.writeln(" source_folder: %s" % it["source_folder"], - Color.BRIGHT_GREEN) - if show("build_folder") and "build_folder" in it: - self._out.writeln(" build_folder: %s" % it["build_folder"], - Color.BRIGHT_GREEN) - if show("package_folder") and "package_folder" in it: - self._out.writeln(" package_folder: %s" % it["package_folder"], - Color.BRIGHT_GREEN) + _print("export_folder") + _print("source_folder") + _print("build_folder") + _print("package_folder") if show("remote") and is_ref: if "remote" in it: @@ -81,32 +79,30 @@ def show(field): else: self._out.writeln(" Remote: None", Color.BRIGHT_GREEN) - if show("url") and "url" in it: - self._out.writeln(" URL: %s" % it["url"], Color.BRIGHT_GREEN) - if show("homepage") and "homepage" in it: - self._out.writeln(" Homepage: %s" % it["homepage"], Color.BRIGHT_GREEN) + _print("url", name="URL") + _print("homepage", name="Homepage") + if show("license") and "license" in it: licenses_str = ", ".join(it["license"]) lead_str = "Licenses" if len(it["license"]) > 1 else "License" self._out.writeln(" %s: %s" % (lead_str, licenses_str), Color.BRIGHT_GREEN) - if show("author") and "author" in it: - self._out.writeln(" Author: %s" % it["author"], Color.BRIGHT_GREEN) + + _print("author", name="Author") + if show("topics") and "topics" in it: self._out.writeln(" Topics: %s" % ", ".join(it["topics"]), Color.BRIGHT_GREEN) - if show("recipe") and "recipe" in it: - self._out.writeln(" Recipe: %s" % it["recipe"]) - if show("revision") and "revision" in it: - self._out.writeln(" Revision: %s" % it["revision"]) - if show("binary") and "binary" in it: - self._out.writeln(" Binary: %s" % it["binary"]) + + _print("recipe", name="Recipe", color=None) + _print("revision", name="Revision", color=None) + _print("binary", name="Binary", color=None) + if show("binary_remote") and is_ref: if "binary_remote" in it: self._out.writeln(" Binary remote: %s" % it["binary_remote"]) else: self._out.writeln(" Binary remote: None") - if show("date") and "creation_date" in it: - self._out.writeln(" Creation date: %s" % it["creation_date"], Color.BRIGHT_GREEN) + _print("creation_date", show_field="date", name="Creation date") if show("required") and "required_by" in it: self._out.writeln(" Required by:", Color.BRIGHT_GREEN) From 46d2a939c7dd6b5b3916fea2b9d164ee8839972e Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 22 Jan 2019 12:46:16 +0100 Subject: [PATCH 03/11] typo --- conans/test/functional/command/info_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/test/functional/command/info_test.py b/conans/test/functional/command/info_test.py index dec1eef2919..0333139b57d 100644 --- a/conans/test/functional/command/info_test.py +++ b/conans/test/functional/command/info_test.py @@ -297,7 +297,7 @@ def info_virtual_test(self): self.assertNotIn("virtual", self.client.out) self.assertNotIn("Required", self.client.out) - def test_reuse_test(self): + def reuse_test(self): self.client = TestClient() self._create("Hello0", "0.1") self._create("Hello1", "0.1", ["Hello0/0.1@lasote/stable"]) From 102566eab7aff205968489040eab514f04b45a5b Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 22 Jan 2019 13:00:51 +0100 Subject: [PATCH 04/11] cannot get paths from editable packages (but still need to raise if those are requested --- conans/client/command.py | 2 +- conans/client/conan_command_output.py | 10 +++++----- .../functional/editable/commands/info_on_child_test.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/conans/client/command.py b/conans/client/command.py index c292493f10b..663e41ba4be 100644 --- a/conans/client/command.py +++ b/conans/client/command.py @@ -558,7 +558,7 @@ def info(self, *args): self._outputer.info_graph(args.graph, deps_graph, get_cwd()) elif args.json: json_arg = True if args.json == "1" else args.json - self._outputer.json_info(deps_graph, json_arg, get_cwd()) + self._outputer.json_info(deps_graph, json_arg, get_cwd(), show_paths=args.paths) else: self._outputer.info(deps_graph, only, args.package_filter, args.paths) diff --git a/conans/client/conan_command_output.py b/conans/client/conan_command_output.py index 1fcbcb206a0..d77614e18b7 100644 --- a/conans/client/conan_command_output.py +++ b/conans/client/conan_command_output.py @@ -99,7 +99,7 @@ def json_nodes_to_build(self, nodes_to_build, json_output, cwd): self.user_io.out.writeln("") self.user_io.out.info("JSON file created at '%s'" % json_output) - def _grab_info_data(self, deps_graph): + def _grab_info_data(self, deps_graph, grab_paths): """ Convert 'deps_graph' into consumible information for json and cli """ compact_nodes = OrderedDict() for node in sorted(deps_graph.nodes): @@ -123,7 +123,7 @@ def _grab_info_data(self, deps_graph): item_data["build_id"] = build_id(conanfile) # Paths - if isinstance(ref, ConanFileReference): + if isinstance(ref, ConanFileReference) and grab_paths: item_data["export_folder"] = self.cache.export(ref) item_data["source_folder"] = self.cache.source(ref, conanfile.short_paths) if isinstance(self.cache, SimplePaths): @@ -195,7 +195,7 @@ def _add_if_exists(attrib, as_list=False): return ret def info(self, deps_graph, only, package_filter, show_paths): - data = self._grab_info_data(deps_graph) + data = self._grab_info_data(deps_graph, grab_paths=show_paths) Printer(self.user_io.out).print_info(data, only, package_filter=package_filter, show_paths=show_paths) @@ -212,8 +212,8 @@ def info_graph(self, graph_filename, deps_graph, cwd): graph_filename = os.path.join(cwd, graph_filename) grapher.graph_file(graph_filename) - def json_info(self, deps_graph, json_output, cwd): - data = self._grab_info_data(deps_graph) + def json_info(self, deps_graph, json_output, cwd, show_paths): + data = self._grab_info_data(deps_graph, grab_paths=show_paths) json_str = json.dumps(data) if json_output is True: diff --git a/conans/test/functional/editable/commands/info_on_child_test.py b/conans/test/functional/editable/commands/info_on_child_test.py index 0ecc1651d57..e4cf684917b 100644 --- a/conans/test/functional/editable/commands/info_on_child_test.py +++ b/conans/test/functional/editable/commands/info_on_child_test.py @@ -53,8 +53,8 @@ def tearDown(self): self.assertFalse(self.t.cache.installed_as_editable(self.ref)) self.assertFalse(os.listdir(self.t.cache.conan(self.ref))) - @parameterized.expand([(True, ), (False, )]) - def test_no_args(self, use_local_path): + #@parameterized.expand([(True, ), (False, )]) + def test_no_args(self, use_local_path=True): args = "." if use_local_path else self.ref_child project_name = "conanfile.py" if use_local_path else self.ref_child From 7827f296feaa54dde3bb3f1ef8d33a8fdc77e5c1 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 22 Jan 2019 13:08:49 +0100 Subject: [PATCH 05/11] cannot check hashes, those are different on win/linux --- conans/test/functional/command/info_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/conans/test/functional/command/info_test.py b/conans/test/functional/command/info_test.py index 0333139b57d..92a93a078c4 100644 --- a/conans/test/functional/command/info_test.py +++ b/conans/test/functional/command/info_test.py @@ -384,7 +384,7 @@ def clean_output(output): self.assertIn(expected_output, clean_output(self.client.user_io.out)) - def test_json_info_outpus(self): + def test_json_info_outputs(self): self.client = TestClient() self._create("LibA", "0.1") self._create("LibE", "0.1") @@ -403,7 +403,8 @@ def test_json_info_outpus(self): content = json.loads(load(json_file)) self.assertEqual(content[0]["reference"], "LibA/0.1@lasote/stable") self.assertEqual(content[0]["license"][0], "MIT") - self.assertEqual(content[1]["id"], "c4ec2bf350e2a02405029ab366535e26372a4f63") + self.assertEqual(content[1]["url"], "myurl") + self.assertEqual(content[1]["required_by"][0], "conanfile.py (LibD/0.1@None/None)") def build_order_test(self): self.client = TestClient() From 432cf2df46a1e8801efaadd20e9b909e0d2f293c Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 22 Jan 2019 13:38:14 +0100 Subject: [PATCH 06/11] unify common code in a single function --- conans/client/conan_command_output.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/conans/client/conan_command_output.py b/conans/client/conan_command_output.py index d77614e18b7..60350092c8f 100644 --- a/conans/client/conan_command_output.py +++ b/conans/client/conan_command_output.py @@ -86,8 +86,7 @@ def _read_dates(self, deps_graph): def nodes_to_build(self, nodes_to_build): self.user_io.out.info(", ".join(str(n) for n in nodes_to_build)) - def json_nodes_to_build(self, nodes_to_build, json_output, cwd): - data = [str(n) for n in nodes_to_build] + def _handle_json_output(self, data, json_output, cwd): json_str = json.dumps(data) if json_output is True: @@ -99,6 +98,10 @@ def json_nodes_to_build(self, nodes_to_build, json_output, cwd): self.user_io.out.writeln("") self.user_io.out.info("JSON file created at '%s'" % json_output) + def json_nodes_to_build(self, nodes_to_build, json_output, cwd): + data = [str(n) for n in nodes_to_build] + self._handle_json_output(data, json_output, cwd) + def _grab_info_data(self, deps_graph, grab_paths): """ Convert 'deps_graph' into consumible information for json and cli """ compact_nodes = OrderedDict() @@ -214,16 +217,7 @@ def info_graph(self, graph_filename, deps_graph, cwd): def json_info(self, deps_graph, json_output, cwd, show_paths): data = self._grab_info_data(deps_graph, grab_paths=show_paths) - json_str = json.dumps(data) - - if json_output is True: - self.user_io.out.write(json_str) - else: - if not os.path.isabs(json_output): - json_output = os.path.join(cwd, json_output) - save(json_output, json.dumps(data)) - self.user_io.out.writeln("") - self.user_io.out.info("JSON file created at '%s'" % json_output) + self._handle_json_output(data, json_output, cwd) def print_search_references(self, search_info, pattern, raw, all_remotes_search): printer = Printer(self.user_io.out) From 9c8c785732ae3ae72191c59d66e8b404a392eb78 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Wed, 23 Jan 2019 15:55:58 +0100 Subject: [PATCH 07/11] typo --- .../test/functional/editable/commands/info_on_child_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conans/test/functional/editable/commands/info_on_child_test.py b/conans/test/functional/editable/commands/info_on_child_test.py index e4cf684917b..0ecc1651d57 100644 --- a/conans/test/functional/editable/commands/info_on_child_test.py +++ b/conans/test/functional/editable/commands/info_on_child_test.py @@ -53,8 +53,8 @@ def tearDown(self): self.assertFalse(self.t.cache.installed_as_editable(self.ref)) self.assertFalse(os.listdir(self.t.cache.conan(self.ref))) - #@parameterized.expand([(True, ), (False, )]) - def test_no_args(self, use_local_path=True): + @parameterized.expand([(True, ), (False, )]) + def test_no_args(self, use_local_path): args = "." if use_local_path else self.ref_child project_name = "conanfile.py" if use_local_path else self.ref_child From cf3ed895e361373248aaf7fd9dbff212ddfe69eb Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Fri, 25 Jan 2019 13:48:13 +0100 Subject: [PATCH 08/11] output to json and graph from the same command --- conans/client/command.py | 5 +++-- conans/paths.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/conans/client/command.py b/conans/client/command.py index 663e41ba4be..10561c344dd 100644 --- a/conans/client/command.py +++ b/conans/client/command.py @@ -556,10 +556,11 @@ def info(self, *args): if args.graph: self._outputer.info_graph(args.graph, deps_graph, get_cwd()) - elif args.json: + if args.json: json_arg = True if args.json == "1" else args.json self._outputer.json_info(deps_graph, json_arg, get_cwd(), show_paths=args.paths) - else: + + if not args.graph and not args.json: self._outputer.info(deps_graph, only, args.package_filter, args.paths) def source(self, *args): diff --git a/conans/paths.py b/conans/paths.py index 3671f213168..336ab9be447 100644 --- a/conans/paths.py +++ b/conans/paths.py @@ -7,7 +7,7 @@ from conans.util.files import rmdir if platform.system() == "Windows": - from conans.util.windows import conan_expand_user, rm_conandir, path_shortener + from conans.util.windows import conan_expand_user, rm_conandir, path_shortener else: def path_shortener(x, _): return x From 325f12df100b1a329330f47f5f993b1cf97040ca Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Mon, 28 Jan 2019 10:26:04 +0100 Subject: [PATCH 09/11] remove duplicated var --- conans/client/conan_command_output.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conans/client/conan_command_output.py b/conans/client/conan_command_output.py index 60350092c8f..e33e4d2e948 100644 --- a/conans/client/conan_command_output.py +++ b/conans/client/conan_command_output.py @@ -137,7 +137,6 @@ def _grab_info_data(self, deps_graph, grab_paths): pref = PackageReference(ref, bid) item_data["build_folder"] = self.cache.build(pref, conanfile.short_paths) - package_id = conanfile.info.package_id() pref = PackageReference(ref, package_id) item_data["package_folder"] = self.cache.package(pref, conanfile.short_paths) From 50bd67730a1abecff4b8c74ee839f971689ef60f Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Mon, 28 Jan 2019 11:20:12 +0100 Subject: [PATCH 10/11] remove redundant call --- conans/client/conan_command_output.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/conans/client/conan_command_output.py b/conans/client/conan_command_output.py index e33e4d2e948..b63c48c0a6c 100644 --- a/conans/client/conan_command_output.py +++ b/conans/client/conan_command_output.py @@ -131,9 +131,7 @@ def _grab_info_data(self, deps_graph, grab_paths): item_data["source_folder"] = self.cache.source(ref, conanfile.short_paths) if isinstance(self.cache, SimplePaths): # @todo: check if this is correct or if it must always be package_id() - bid = build_id(conanfile) - if not bid: - bid = conanfile.info.package_id() + bid = build_id(conanfile) or package_id pref = PackageReference(ref, bid) item_data["build_folder"] = self.cache.build(pref, conanfile.short_paths) From 8c17b8e6126b05a553a874a23521c16e23779e22 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Mon, 28 Jan 2019 11:20:22 +0100 Subject: [PATCH 11/11] pep8 --- conans/util/locks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/util/locks.py b/conans/util/locks.py index c9f7d20f2b2..f7b8f34d3b9 100644 --- a/conans/util/locks.py +++ b/conans/util/locks.py @@ -50,7 +50,7 @@ def __init__(self, folder, locked_item, output): @property def files(self): - return (self._count_file, self._count_lock_file) + return self._count_file, self._count_lock_file def _info_locked(self): if self._first_lock: