forked from conan-io/conan
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathworkspace.py
226 lines (199 loc) · 10.8 KB
/
workspace.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import json
import os
from conan.api.conan_api import ConanAPI
from conan.api.output import ConanOutput, cli_out_write
from conan.api.subapi.workspace import WorkspaceAPI
from conan.cli import make_abs_path
from conan.cli.args import add_reference_args, add_common_install_arguments, add_lockfile_args
from conan.cli.command import conan_command, conan_subcommand
from conan.cli.commands.list import print_serial
from conan.cli.printers import print_profiles
from conan.cli.printers.graph import print_graph_packages, print_graph_basic
from conan.errors import ConanException
@conan_subcommand(formatters={"text": cli_out_write})
def workspace_root(conan_api: ConanAPI, parser, subparser, *args): # noqa
"""
Return the folder containing the conanws.py/conanws.yml workspace file
"""
ws = conan_api.workspace
if not ws.folder():
raise ConanException("No workspace defined, conanws.py file not found")
return ws.folder()
@conan_subcommand()
def workspace_open(conan_api: ConanAPI, parser, subparser, *args):
"""
Open specific references
"""
subparser.add_argument("reference",
help="Open this package source repository")
group = subparser.add_mutually_exclusive_group()
group.add_argument("-r", "--remote", action="append", default=None,
help='Look in the specified remote or remotes server')
group.add_argument("-nr", "--no-remote", action="store_true",
help='Do not use remote, resolve exclusively in the cache')
args = parser.parse_args(*args)
remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
cwd = os.getcwd()
conan_api.workspace.open(args.reference, remotes=remotes, cwd=cwd)
@conan_subcommand()
def workspace_add(conan_api: ConanAPI, parser, subparser, *args):
"""
Add packages to current workspace
"""
subparser.add_argument('path', nargs="?",
help='Path to the package folder in the user workspace')
add_reference_args(subparser)
subparser.add_argument("--ref", help="Open and add this reference")
subparser.add_argument("-of", "--output-folder",
help='The root output folder for generated and build files')
group = subparser.add_mutually_exclusive_group()
group.add_argument("-r", "--remote", action="append", default=None,
help='Look in the specified remote or remotes server')
group.add_argument("-nr", "--no-remote", action="store_true",
help='Do not use remote, resolve exclusively in the cache')
subparser.add_argument("--product", action="store_true", help="Add the package as a product")
args = parser.parse_args(*args)
if args.path and args.ref:
raise ConanException("Do not use both 'path' and '--ref' argument")
remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
cwd = os.getcwd()
path = args.path
if args.ref:
# TODO: Use path here to open in this path
path = conan_api.workspace.open(args.ref, remotes, cwd=cwd)
ref = conan_api.workspace.add(path,
args.name, args.version, args.user, args.channel,
cwd, args.output_folder, remotes=remotes, product=args.product)
ConanOutput().success("Reference '{}' added to workspace".format(ref))
@conan_subcommand()
def workspace_remove(conan_api: ConanAPI, parser, subparser, *args):
"""
Remove packages from the current workspace
"""
subparser.add_argument('path',
help='Path to the package folder in the user workspace')
args = parser.parse_args(*args)
removed = conan_api.workspace.remove(make_abs_path(args.path))
ConanOutput().info(f"Removed from workspace: {removed}")
def print_json(data):
results = data["info"]
myjson = json.dumps(results, indent=4)
cli_out_write(myjson)
def _print_workspace_info(data):
print_serial(data["info"])
@conan_subcommand(formatters={"text": _print_workspace_info, "json": print_json})
def workspace_info(conan_api: ConanAPI, parser, subparser, *args): # noqa
"""
Display info for current workspace
"""
return {"info": conan_api.workspace.info()}
@conan_subcommand()
def workspace_build(conan_api: ConanAPI, parser, subparser, *args):
"""
Build the current workspace, starting from the "products"
"""
subparser.add_argument("path", nargs="?",
help='Path to a package folder in the user workspace')
add_common_install_arguments(subparser)
add_lockfile_args(subparser)
args = parser.parse_args(*args)
# Basic collaborators: remotes, lockfile, profiles
remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
overrides = eval(args.lockfile_overrides) if args.lockfile_overrides else None
# The lockfile by default if not defined will be read from the root workspace folder
ws_folder = conan_api.workspace.folder()
lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, conanfile_path=ws_folder,
cwd=None,
partial=args.lockfile_partial, overrides=overrides)
profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)
print_profiles(profile_host, profile_build)
build_mode = args.build or []
if "editable" not in build_mode:
ConanOutput().info("Adding '--build=editable' as build mode")
build_mode.append("editable")
if args.path:
products = [args.path]
else: # all products
products = conan_api.workspace.products
if not products:
raise ConanException("There are no products defined in the workspace, can't build\n"
"You can use 'conan build <path> --build=editable' to build")
ConanOutput().title(f"Building workspace products {products}")
editables = conan_api.workspace.editable_packages
# TODO: This has to be improved to avoid repetition when there are multiple products
for product in products:
ConanOutput().subtitle(f"Building workspace product: {product}")
product_ref = conan_api.workspace.editable_from_path(product)
if product_ref is None:
raise ConanException(f"Product '{product}' not defined in the workspace as editable")
editable = editables[product_ref]
editable_path = editable["path"]
deps_graph = conan_api.graph.load_graph_consumer(editable_path, None, None, None, None,
profile_host, profile_build, lockfile,
remotes, args.update)
deps_graph.report_graph_error()
print_graph_basic(deps_graph)
conan_api.graph.analyze_binaries(deps_graph, build_mode, remotes=remotes, update=args.update,
lockfile=lockfile)
print_graph_packages(deps_graph)
conan_api.install.install_binaries(deps_graph=deps_graph, remotes=remotes)
conan_api.install.install_consumer(deps_graph, None, os.path.dirname(editable_path),
editable.get("output_folder"))
ConanOutput().title(f"Calling build() for the product {product_ref}")
conanfile = deps_graph.root.conanfile
conan_api.local.build(conanfile)
@conan_subcommand()
def workspace_install(conan_api: ConanAPI, parser, subparser, *args):
"""
Install the workspace as a monolith, installing only external dependencies to the workspace,
generating a single result (generators, etc) for the whole workspace.
"""
subparser.add_argument("path", nargs="*",
help="Install only these editable packages, not all")
subparser.add_argument("-g", "--generator", action="append", help='Generators to use')
subparser.add_argument("-of", "--output-folder",
help='The root output folder for generated and build files')
subparser.add_argument("--envs-generation", default=None, choices=["false"],
help="Generation strategy for virtual environment files for the root")
add_common_install_arguments(subparser)
add_lockfile_args(subparser)
args = parser.parse_args(*args)
# Basic collaborators: remotes, lockfile, profiles
remotes = conan_api.remotes.list(args.remote) if not args.no_remote else []
overrides = eval(args.lockfile_overrides) if args.lockfile_overrides else None
# The lockfile by default if not defined will be read from the root workspace folder
ws_folder = conan_api.workspace.folder()
lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, conanfile_path=ws_folder,
cwd=None,
partial=args.lockfile_partial, overrides=overrides)
profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args)
print_profiles(profile_host, profile_build)
conan_api.workspace.info() # FIXME: Just to force error if WS not enabled
# Build a dependency graph with all editables as requirements
requires = conan_api.workspace.select_editables(args.path)
if not requires:
raise ConanException("This workspace cannot be installed, it doesn't have any editable")
deps_graph = conan_api.graph.load_graph_requires(requires, [],
profile_host, profile_build, lockfile,
remotes, args.build, args.update)
deps_graph.report_graph_error()
print_graph_basic(deps_graph)
# Collapsing the graph
ws_graph = conan_api.workspace.collapse_editables(deps_graph, profile_host, profile_build)
ConanOutput().subtitle("Collapsed graph")
print_graph_basic(ws_graph)
conan_api.graph.analyze_binaries(ws_graph, args.build, remotes=remotes, update=args.update,
lockfile=lockfile)
print_graph_packages(ws_graph)
conan_api.install.install_binaries(deps_graph=ws_graph, remotes=remotes)
output_folder = make_abs_path(args.output_folder) if args.output_folder else None
conan_api.install.install_consumer(ws_graph, args.generator, ws_folder, output_folder,
envs_generation=args.envs_generation)
@conan_command(group="Consumer")
def workspace(conan_api, parser, *args): # noqa
"""
Manage Conan workspaces (group of packages in editable mode)
"""
if (WorkspaceAPI.TEST_ENABLED or os.getenv("CONAN_WORKSPACE_ENABLE")) != "will_break_next":
raise ConanException("Workspace command disabled without CONAN_WORKSPACE_ENABLE env var,"
"please read the docs about this 'incubating' feature")