From 74e7a97f8f43a3b3c168de72b1834c1407eb0a09 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 11 Mar 2024 09:52:18 +0100 Subject: [PATCH 01/13] proposal for new graph html --- conan/cli/formatters/graph/graph.py | 24 +-- conan/cli/formatters/graph/info_graph_html.py | 137 +++++++----------- 2 files changed, 64 insertions(+), 97 deletions(-) diff --git a/conan/cli/formatters/graph/graph.py b/conan/cli/formatters/graph/graph.py index 08711a47ac4..8bd7a1045ca 100644 --- a/conan/cli/formatters/graph/graph.py +++ b/conan/cli/formatters/graph/graph.py @@ -3,7 +3,7 @@ from jinja2 import Template, select_autoescape -from conan.api.output import cli_out_write +from conan.api.output import cli_out_write, ConanOutput from conan.cli.formatters.graph.graph_info_text import filter_graph from conan.cli.formatters.graph.info_graph_dot import graph_info_dot from conan.cli.formatters.graph.info_graph_html import graph_info_html @@ -55,7 +55,14 @@ class _Grapher(object): def __init__(self, deps_graph): self._deps_graph = deps_graph self.node_map, self.edges = self._build_graph() - self.nodes = self.node_map.values() + self._nodes = self.node_map.values() + + @property + def nodes(self): + ConanOutput().warning("--format=html rendering using 'graph' object is deprecated and " + "will be removed in future 2.X version. Please upgrade your template " + "to use 'deps_graph' instead", warn_tag="deprecated") + return self._nodes def _build_graph(self): graph_nodes = self._deps_graph.by_levels() @@ -88,21 +95,18 @@ def binary_color(node): def _render_graph(graph, error, template, template_folder): + deps_graph = json.dumps(graph.serialize()) graph = _Grapher(graph) from conans import __version__ as client_version - template = Template(template, autoescape=select_autoescape(['html', 'xml'])) - return template.render(graph=graph, error=error, base_template_path=template_folder, - version=client_version) + template = Template(template) + return template.render(deps_graph=deps_graph, graph=graph, error=error, + base_template_path=template_folder, version=client_version) def format_graph_html(result): graph = result["graph"] conan_api = result["conan_api"] - package_filter = result["package_filter"] - serial = graph.serialize() - # TODO: This is not used, it is necessary to update the renderings to use the serialized graph - # instead of the native graph - serial = filter_graph(serial, package_filter) + template_folder = os.path.join(conan_api.cache_folder, "templates") user_template = os.path.join(template_folder, "graph.html") template = load(user_template) if os.path.isfile(user_template) else graph_info_html diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py index 40f2bbc00a4..903e3a08575 100644 --- a/conan/cli/formatters/graph/info_graph_html.py +++ b/conan/cli/formatters/graph/info_graph_html.py @@ -31,8 +31,15 @@ } + +
+
Package info: no package selected
-
+
+
+ + +
+
+ + +
+
Package info: no package selected
+ +
+
+
+ + + +
+
+
+

+ Conan v2.2.0-dev JFrog LTD. https://conan.io +

+
+
+
+ From b8847a2ea77a42293e0f620bd4f50e852a4a5fb2 Mon Sep 17 00:00:00 2001 From: memsharded Date: Thu, 14 Mar 2024 23:42:56 +0100 Subject: [PATCH 03/13] wip --- conan/cli/formatters/graph/info_graph_html.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py index 168e5356f1f..b99a7042a07 100644 --- a/conan/cli/formatters/graph/info_graph_html.py +++ b/conan/cli/formatters/graph/info_graph_html.py @@ -5,9 +5,6 @@ - -
-
-
-
+
+
+
+
+
+ + +
- -
Package info: no package selected
- +
+ +
+
+ + +
+
Package info: Click on one package to show information
-
@@ -38,6 +38,10 @@
+
+
+ +
@@ -57,6 +61,7 @@ var hide_test = false; var search_pkg = null; var collapse_build = false; + var show_package_type = false; var color_map = {Cache: "SkyBlue", Download: "LightGreen", Build: "Yellow", @@ -67,16 +72,23 @@ EditableBuild: "Cyan", Invalid: "Red", Platform: "Violet"}; + var global_edges = {}; function define_data(){ var nodes = []; var edges = []; var collapsed_build = {}; var targets = {}; + global_edges = {}; + var edge_counter = 0; for (const [node_id, node] of Object.entries(graph_data["nodes"])) { if (node.context == "build" && hide_build) continue; if (node.test && hide_test) continue; - const shape = node.context == "build" ? "ellipse" : node.test ? "hexagon" : "box"; - var label = node["name"] + "/" + node["version"]; + const shape = node.context == "build" || node.test ? "ellipse" : "box"; + if (node["name"]) + var label = node["name"] + "/" + node["version"]; + else + var label = node.recipe == "Consumer"? "conanfile": "CLI"; + var color = color_map[node.binary] if (collapse_build) { var existing = collapsed_build[label]; @@ -84,28 +96,43 @@ if (existing) continue; collapsed_build[label] = node_id; } - if (node["name"] == search_pkg){ + if (node["name"] && search_pkg && node["name"].match(search_pkg)){ borderWidth = 3; - borderColor = "Red" + borderColor = "Magenta" } else { borderWidth = 1; borderColor = "SkyBlue"; } + if (node.test) { + font = {size: 35, background: "grey"}; + shapeProperties = {borderDashes: true}; + } + else { + shapeProperties = {}; + } + if (show_package_type) { + label = "" + label + "\n" + "" + node.package_type + ""; + } nodes.push({ id: node_id, + font: {multi: 'html'}, label: label, shape: shape, + shapeProperties: shapeProperties, borderWidth: borderWidth, color: {border: borderColor, background: color, - highlight: {background: color, border: borderColor}}, + highlight: {background: color, border: "Blue"}}, }); } for (const [node_id, node] of Object.entries(graph_data["nodes"])) { for (const [dep_id, dep] of Object.entries(node["dependencies"])) { if (dep.direct){ var target_id = targets[dep_id] || dep_id; - edges.push({from: node_id, to: target_id, color: "SkyBlue"}); + edges.push({id: edge_counter, from: node_id, to: target_id, + color: {color: "SkyBlue", highlight: "Blue"}}); + global_edges[edge_counter] = dep; + edge_counter++; } } } @@ -115,8 +142,8 @@ return data; }; function define_legend() { - var x = -300; - var y = -200; + var x = 0; + var y = 0; var step = 250; var legend_nodes = []; legend_nodes.push({id: 0, x: x, y: y, shape: "box", font: {size: 35}, @@ -156,18 +183,16 @@ }, layout: { "hierarchical": { - "enabled": true, - "sortMethod": "directed", - "direction": "DU", + enabled: true, + sortMethod: "directed", + direction: "DU", nodeSpacing: 170, blockShifting: true, edgeMinimization: true, shakeTowards: "roots", } }, - physics: { - enabled: false, - }, + physics: { enabled: false}, configure: { enabled: true, filter: 'layout physics', @@ -185,52 +210,48 @@ network.on('click', function (properties) { var ids = properties.nodes; + var ids_edges = properties.edges; var control = document.getElementById("details"); while (control.firstChild) { control.removeChild(control.firstChild); } - if(ids[0]) { - selected_node = graph_data["nodes"][ids[0]] - let ul = document.createElement('ul'); - for (const [key, value] of Object.entries(selected_node)) { - if (value) { - let li = document.createElement('li'); - li.innerHTML = ""+ key +": " + value; - ul.appendChild(li); - } - } - control.appendChild(ul); + if(ids[0] || ids_edges[0]) { + selected = graph_data["nodes"][ids[0]] || global_edges[ids_edges[0]]; + let div = document.createElement('div'); + let f = Object.fromEntries(Object.entries(selected).filter(([_, v]) => v != null)); + div.innerHTML = "
" + JSON.stringify(f, undefined, 2) + "
"; + control.appendChild(div); } else { - control.innerHTML = "Package info: No package selected"; + control.innerHTML = "Info: Click on a package or edge for more info"; } }); function draw() { + var scale = network.getScale(); + var viewPos = network.getViewPosition(); + data = define_data(); + network.setData(data); network.redraw(); + network.moveTo({position: viewPos, scale: scale}); } function switchBuild() { hide_build = !hide_build; - data = define_data(); - network.setData(data); draw(); } function switchTest() { hide_test = !hide_test; - data = define_data(); - network.setData(data); draw(); } function collapseBuild() { collapse_build = !collapse_build; - console.log("collapsing build"); - data = define_data(); - network.setData(data); draw(); } function searchPackage(e) { search_pkg = e.value; - data = define_data(); - network.setData(data); + draw(); + } + function showPackageType(e) { + show_package_type = !show_package_type; draw(); } function showhideclass(id) { @@ -244,14 +265,5 @@ }); -
-
-
-

- Conan v{{ version }} JFrog LTD. https://conan.io -

-
-
-
""" diff --git a/graph.html b/graph.html index 6a1e4668fa5..e69de29bb2d 100644 --- a/graph.html +++ b/graph.html @@ -1,189 +0,0 @@ - - - - - - - - - - -
-
-
-
- - -
-
- - -
- -
Package info: no package selected
- - -
-
-
- - - -
-
-
-

- Conan v2.2.0-dev JFrog LTD. https://conan.io -

-
-
-
- From faccbb678351cf7b708f3330107b12e28a1030bc Mon Sep 17 00:00:00 2001 From: memsharded Date: Sun, 17 Mar 2024 02:17:42 +0100 Subject: [PATCH 07/13] wip --- conan/cli/formatters/graph/info_graph_html.py | 87 +++++++++++---- conans/client/graph/graph.py | 2 + conans/client/graph/graph_error.py | 9 +- .../command/info/test_graph_info_graphical.py | 105 +----------------- 4 files changed, 80 insertions(+), 123 deletions(-) diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py index 90546c77caa..4a510680c9a 100644 --- a/conan/cli/formatters/graph/info_graph_html.py +++ b/conan/cli/formatters/graph/info_graph_html.py @@ -52,6 +52,7 @@
Package info: Click on one package to show information
+
@@ -79,44 +80,53 @@ var collapsed_build = {}; var targets = {}; global_edges = {}; + global_nodes = {}; var edge_counter = 0; + if (graph_data["error"]){ + if (graph_data["error"]["type"] == "conflict"){ + var conflict = graph_data["error"]["conflict"]; // id and ref + var branch1 = graph_data["error"]["branch1"]; // id and ref + var branch2 = graph_data["error"]["branch2"]; // id and ref + } + } for (const [node_id, node] of Object.entries(graph_data["nodes"])) { if (node.context == "build" && hide_build) continue; if (node.test && hide_test) continue; + if (collapse_build) { + var existing = collapsed_build[label]; + targets[node_id] = existing; + if (existing) continue; + collapsed_build[label] = node_id; + } const shape = node.context == "build" || node.test ? "ellipse" : "box"; if (node["name"]) var label = node["name"] + "/" + node["version"]; else var label = node.recipe == "Consumer"? "conanfile": "CLI"; + if (show_package_type) { + label = "" + label + "\n" + "" + node.package_type + ""; + } + borderWidth = 1; + borderColor = "SkyBlue"; + font = {multi: 'html'}; + shapeProperties = {}; var color = color_map[node.binary] - if (collapse_build) { - var existing = collapsed_build[label]; - targets[node_id] = existing; - if (existing) continue; - collapsed_build[label] = node_id; + if (conflict && conflict.id == node_id){ + font.color = "white"; + color = "Black"; } if (node["name"] && search_pkg && node["name"].match(search_pkg)){ borderWidth = 3; borderColor = "Magenta" } - else { - borderWidth = 1; - borderColor = "SkyBlue"; - } if (node.test) { - font = {size: 35, background: "grey"}; + font.background = "lightgrey"; shapeProperties = {borderDashes: true}; } - else { - shapeProperties = {}; - } - if (show_package_type) { - label = "" + label + "\n" + "" + node.package_type + ""; - } nodes.push({ id: node_id, - font: {multi: 'html'}, + font: font, label: label, shape: shape, shapeProperties: shapeProperties, @@ -136,6 +146,30 @@ } } } + if (conflict && branch2) { + nodes.push({ + id: "conflict", + font: {color: "white"}, + label: conflict.ref, + shape: "circle", + color: {background: "black", + highlight: {background: "black", border: "Blue"}}, + }); + nodes.push({ + id: "branch2", + font: {color: "white"}, + label: branch2.ref, + shape: "box", + color: {background: "black", + highlight: {background: "black", border: "Blue"}}, + }); + edges.push({from: branch2.id, to: "branch2", + color: {color: "SkyBlue", highlight: "Blue"}}); + edges.push({from: "branch2", to: "conflict", + color: {color: "Red", highlight: "Red"}}); + edges.push({from: conflict.id, to: "conflict", + color: {color: "Red", highlight: "Red"}}); + } var nodes = new vis.DataSet(nodes); var edges = new vis.DataSet(edges); var data = {nodes: nodes, edges: edges}; @@ -152,7 +186,7 @@ legend_nodes.push({id: 1, x: x + step, y: y, font: {size: 35}, shape: "ellipse", label: "tool-require", }); - legend_nodes.push({id: 2, x: x + 2* step, y: y, font: {size: 35, background: "grey"}, + legend_nodes.push({id: 2, x: x + 2* step, y: y, font: {size: 35, background: "lightgrey"}, shape: "ellipse", shapeProperties: {borderDashes: true}, label: "test-require", }) @@ -164,9 +198,20 @@ }); counter++; } + legend_nodes.push({x: x + counter*step, y: y, shape: "box", + label: "conflict", + font: {size: 35, color: "white"}, + color: {border: "SkyBlue", background: "Black"} + }); + return {nodes: new vis.DataSet(legend_nodes)}; } - + var error = document.getElementById("error"); + if (graph_data["error"]){ + let div = document.createElement('div'); + div.innerHTML = "
Error in the graph: " + JSON.stringify(graph_data["error"], undefined, 2) + "
"; + error.appendChild(div); + } var container = document.getElementById('mynetwork'); var controls = document.getElementById('controls'); var legend_container = document.getElementById('mylegend'); @@ -178,9 +223,7 @@ arrows: { to: {enabled: true} }, smooth: { enabled: false} }, - nodes: { - font: {'face': 'monospace', 'align': 'left'} - }, + nodes: {font: {'face': 'monospace', 'align': 'left'}}, layout: { "hierarchical": { enabled: true, diff --git a/conans/client/graph/graph.py b/conans/client/graph/graph.py index 63f79a41a34..4fa776dce0e 100644 --- a/conans/client/graph/graph.py +++ b/conans/client/graph/graph.py @@ -1,5 +1,6 @@ from collections import OrderedDict +from conans.client.graph.graph_error import GraphError from conans.model.package_ref import PkgReference from conans.model.recipe_ref import RecipeReference @@ -394,4 +395,5 @@ def serialize(self): result["overrides"] = self.overrides().serialize() result["resolved_ranges"] = {repr(r): s.repr_notime() for r, s in self.resolved_ranges.items()} result["replaced_requires"] = {k: v for k, v in self.replaced_requires.items()} + result["error"] = self.error.serialize() if isinstance(self.error, GraphError) else None return result diff --git a/conans/client/graph/graph_error.py b/conans/client/graph/graph_error.py index 3f5b62409f0..a17ded34672 100644 --- a/conans/client/graph/graph_error.py +++ b/conans/client/graph/graph_error.py @@ -2,7 +2,8 @@ class GraphError(ConanException): - pass + def serialize(self): + return class GraphConflictError(GraphError): @@ -14,6 +15,12 @@ def __init__(self, node, require, prev_node, prev_require, base_previous): self.prev_require = prev_require self.base_previous = base_previous + def serialize(self): + return {"type": "conflict", + "conflict": {"id": self.prev_node.id, "ref": str(self.prev_node.ref)}, + "branch1": {"id": self.base_previous.id, "ref": str(self.base_previous.ref)}, + "branch2": {"id": self.node.id, "ref": str(self.require.ref)}} + def __str__(self): if self.node.ref is not None and self.base_previous.ref is not None: return f"Version conflict: {self.node.ref}->{self.require.ref}, " \ diff --git a/conans/test/integration/command/info/test_graph_info_graphical.py b/conans/test/integration/command/info/test_graph_info_graphical.py index ca1773cf3f9..c7c4496b6da 100644 --- a/conans/test/integration/command/info/test_graph_info_graphical.py +++ b/conans/test/integration/command/info/test_graph_info_graphical.py @@ -1,9 +1,8 @@ import os import textwrap import unittest -from datetime import datetime -from conans import __version__ as client_version + from conans.test.utils.tools import TestClient, GenConanfile @@ -101,66 +100,6 @@ def create_export(testdeps, name): self.client.run("graph info . --format=html") html = self.client.stdout self.assertIn("", html) - self.assertIn("{ from: 0, to: 1 }", html) - self.assertIn("id: 0,\n label: 'hello0/0.1',", html) - self.assertIn("Conan v{} " - " JFrog LTD. https://conan.io" - .format(client_version, datetime.today().year), html) - - def test_info_build_requires(self): - client = TestClient() - client.save({"conanfile.py": GenConanfile()}) - client.run("create . --name=tool --version=0.1 --user=user --channel=channel") - client.run("create . --name=dep --version=0.1 --user=user --channel=channel") - conanfile = GenConanfile().with_require("dep/0.1@user/channel") - client.save({"conanfile.py": conanfile}) - client.run("export . --name=pkg --version=0.1 --user=user --channel=channel") - client.run("export . --name=pkg2 --version=0.1 --user=user --channel=channel") - client.save({"conanfile.txt": "[requires]\npkg/0.1@user/channel\npkg2/0.1@user/channel", - "myprofile": "[tool_requires]\ntool/0.1@user/channel"}, clean_first=True) - client.run("graph info . -pr=myprofile --build=missing --format=html") - html = client.stdout - self.assertIn("html", html) - # To check that this node is not duplicated - self.assertEqual(1, html.count("label: 'dep/0.1'")) - self.assertIn("label: 'pkg2/0.1',\n " - "shape: 'box',\n " - "color: { background: 'Khaki'},", html) - self.assertIn("label: 'pkg/0.1',\n " - "shape: 'box',\n " - "color: { background: 'Khaki'},", html) - self.assertIn("label: 'tool/0.1',\n " - "shape: 'ellipse',\n " - "color: { background: 'SkyBlue'},", html) - - def test_topics_graph(self): - conanfile = textwrap.dedent(""" - from conan import ConanFile - - class MyTest(ConanFile): - name = "pkg" - version = "0.2" - topics = ("foo", "bar", "qux") - """) - - client = TestClient() - client.save({"conanfile.py": conanfile}) - client.run("export . --user=lasote --channel=testing") - - # Topics as tuple - client.run("graph info --requires=pkg/0.2@lasote/testing --format=html") - html_content = client.stdout - self.assertIn("

pkg/0.2@lasote/testing

", html_content) - self.assertIn("
  • topics: foo, bar, qux
  • ", html_content) - - # Topics as a string - conanfile = conanfile.replace("(\"foo\", \"bar\", \"qux\")", "\"foo\"") - client.save({"conanfile.py": conanfile}, clean_first=True) - client.run("export . --user=lasote --channel=testing") - client.run("graph info --requires=pkg/0.2@lasote/testing --format=html") - html_content = client.stdout - self.assertIn("

    pkg/0.2@lasote/testing

    ", html_content) - self.assertIn("
  • topics: foo", html_content) def test_user_templates(): @@ -183,46 +122,12 @@ def test_graph_info_html_error_reporting_output(): tc = TestClient() tc.save({"lib/conanfile.py": GenConanfile("lib"), "ui/conanfile.py": GenConanfile("ui", "1.0").with_requirement("lib/1.0"), - "math/conanfile.py": GenConanfile("math", "1.0").with_requirement("lib/2.0"), - "libiconv/conanfile.py": GenConanfile("libiconv", "1.0").with_requirement("lib/2.0"), - "boost/conanfile.py": GenConanfile("boost", "1.0").with_requirement("libiconv/1.0"), - "openimageio/conanfile.py": GenConanfile("openimageio", "1.0").with_requirement("boost/1.0").with_requirement("lib/1.0")}) + "math/conanfile.py": GenConanfile("math", "1.0").with_requirement("lib/2.0")}) tc.run("export lib/ --version=1.0") tc.run("export lib/ --version=2.0") tc.run("export ui") tc.run("export math") - tc.run("export libiconv") - tc.run("export boost") - tc.run("export openimageio") - - tc.run("graph info --requires=math/1.0 --requires=ui/1.0 --format=html", assert_error=True) - assert "// Add error conflict node" in tc.out - assert "// Add edge from node that introduces the conflict to the new error node" in tc.out - assert "// Add edge from base node to the new error node" not in tc.out - assert "// Add edge from previous node that already had conflicting dependency" in tc.out - - # Ensure mapping is preserved, ui is node id 3 before ordering, but 2 after - assert "id: 2,\n label: 'ui/1.0'" in tc.out - - tc.run("graph info openimageio/ --format=html", assert_error=True) - assert "// Add error conflict node" in tc.out - assert "// Add edge from node that introduces the conflict to the new error node" in tc.out - assert "// Add edge from base node to the new error node" in tc.out - assert "// Add edge from previous node that already had conflicting dependency" not in tc.out - # There used to be a few bugs with weird graphs, check for regressions - assert "jinja2.exceptions.UndefinedError" not in tc.out - assert "from: ," not in tc.out - - -def test_graph_info_html_error_range_quoting(): - tc = TestClient() - tc.save({"zlib/conanfile.py": GenConanfile("zlib"), - "libpng/conanfile.py": GenConanfile("libpng", "1.0").with_requirement("zlib/[>=1.0]")}) - - tc.run("export zlib --version=1.0") - tc.run("export zlib --version=0.1") - tc.run("export libpng") - tc.run("graph info --requires=zlib/0.1 --requires=libpng/1.0 --format=html", assert_error=True) - assert 'zlib/[>=1.0]' not in tc.out - assert r'"zlib/[\u003e=1.0]"' in tc.out + tc.run("graph info --requires=math/1.0 --requires=ui/1.0 --format=html", assert_error=True, + redirect_stdout="graph.html") + print(tc.current_folder) From 4867ff4954e946f49ed96a44b39684dc4a66c7aa Mon Sep 17 00:00:00 2001 From: memsharded Date: Sun, 17 Mar 2024 22:43:55 +0100 Subject: [PATCH 08/13] review --- .../integration/command/info/test_graph_info_graphical.py | 4 +++- graph.html | 0 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 graph.html diff --git a/conans/test/integration/command/info/test_graph_info_graphical.py b/conans/test/integration/command/info/test_graph_info_graphical.py index c7c4496b6da..55737d233ad 100644 --- a/conans/test/integration/command/info/test_graph_info_graphical.py +++ b/conans/test/integration/command/info/test_graph_info_graphical.py @@ -99,6 +99,7 @@ def create_export(testdeps, name): # arbitrary case - file will be named according to argument self.client.run("graph info . --format=html") html = self.client.stdout + # Just make sure it doesn't crash self.assertIn("", html) @@ -130,4 +131,5 @@ def test_graph_info_html_error_reporting_output(): tc.run("graph info --requires=math/1.0 --requires=ui/1.0 --format=html", assert_error=True, redirect_stdout="graph.html") - print(tc.current_folder) + # check that it doesn't crash + # analyze manually the html diff --git a/graph.html b/graph.html deleted file mode 100644 index e69de29bb2d..00000000000 From 22b4e52a6a371cfe9378c1e6beffb835fdb79dd6 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 18 Mar 2024 12:09:09 +0100 Subject: [PATCH 09/13] review --- conan/cli/formatters/graph/info_graph_html.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py index 4a510680c9a..64577d3e62b 100644 --- a/conan/cli/formatters/graph/info_graph_html.py +++ b/conan/cli/formatters/graph/info_graph_html.py @@ -101,6 +101,8 @@ const shape = node.context == "build" || node.test ? "ellipse" : "box"; if (node["name"]) var label = node["name"] + "/" + node["version"]; + else if (node["ref"]) + var label = node["ref"]; else var label = node.recipe == "Consumer"? "conanfile": "CLI"; if (show_package_type) { @@ -124,6 +126,9 @@ font.background = "lightgrey"; shapeProperties = {borderDashes: true}; } + if (node.recipe == "Platform") { + font.background = "Violet"; + } nodes.push({ id: node_id, font: font, @@ -191,6 +196,11 @@ label: "test-require", }) var counter = 3; + legend_nodes.push({x: x + counter*step, y: y, shape: "ellipse", + label: "platform", + font: {size: 35, background: "Violet"}, + }); + counter++; for (const [status, color] of Object.entries(color_map)) { legend_nodes.push({x: x + counter*step, y: y, shape: "box", font: {size: 35}, label: status, @@ -203,7 +213,6 @@ font: {size: 35, color: "white"}, color: {border: "SkyBlue", background: "Black"} }); - return {nodes: new vis.DataSet(legend_nodes)}; } var error = document.getElementById("error"); From a83be701b5db5102539dedb9423c0ad14e46da0e Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 18 Mar 2024 12:26:36 +0100 Subject: [PATCH 10/13] fix --- conan/cli/formatters/graph/info_graph_html.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py index 64577d3e62b..dd76c1586b4 100644 --- a/conan/cli/formatters/graph/info_graph_html.py +++ b/conan/cli/formatters/graph/info_graph_html.py @@ -118,9 +118,9 @@ font.color = "white"; color = "Black"; } - if (node["name"] && search_pkg && node["name"].match(search_pkg)){ + if (search_pkg && label.match(search_pkg)) { borderWidth = 3; - borderColor = "Magenta" + borderColor = "Magenta"; } if (node.test) { font.background = "lightgrey"; From f4efcd554389d07b78b5b9c734a144784e21437d Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 18 Mar 2024 13:09:14 +0100 Subject: [PATCH 11/13] fix --- conan/cli/formatters/graph/info_graph_html.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py index dd76c1586b4..eaee94fdeff 100644 --- a/conan/cli/formatters/graph/info_graph_html.py +++ b/conan/cli/formatters/graph/info_graph_html.py @@ -92,12 +92,6 @@ for (const [node_id, node] of Object.entries(graph_data["nodes"])) { if (node.context == "build" && hide_build) continue; if (node.test && hide_test) continue; - if (collapse_build) { - var existing = collapsed_build[label]; - targets[node_id] = existing; - if (existing) continue; - collapsed_build[label] = node_id; - } const shape = node.context == "build" || node.test ? "ellipse" : "box"; if (node["name"]) var label = node["name"] + "/" + node["version"]; @@ -105,10 +99,15 @@ var label = node["ref"]; else var label = node.recipe == "Consumer"? "conanfile": "CLI"; + if (collapse_build) { + var existing = collapsed_build[label]; + targets[node_id] = existing; + if (existing) continue; + collapsed_build[label] = node_id; + } if (show_package_type) { label = "" + label + "\n" + "" + node.package_type + ""; } - borderWidth = 1; borderColor = "SkyBlue"; font = {multi: 'html'}; From ac6c318c61bbbb8f5a5cfe56da88bababdac1396 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 18 Mar 2024 20:38:13 +0100 Subject: [PATCH 12/13] review --- conan/cli/formatters/graph/info_graph_html.py | 155 +++++++++--------- conans/client/graph/graph_error.py | 9 +- .../command/info/test_graph_info_graphical.py | 15 +- 3 files changed, 95 insertions(+), 84 deletions(-) diff --git a/conan/cli/formatters/graph/info_graph_html.py b/conan/cli/formatters/graph/info_graph_html.py index eaee94fdeff..b4024bfef5a 100644 --- a/conan/cli/formatters/graph/info_graph_html.py +++ b/conan/cli/formatters/graph/info_graph_html.py @@ -1,7 +1,7 @@ graph_info_html = r""" - + @@ -58,12 +58,12 @@