blob: 85f1c07c726623a4e31c3f1d531054b0c5b993c5 [file] [log] [blame]
George Burgess IV18af5632022-08-30 14:10:53 -07001#!/usr/bin/env python3
George Burgess IVbdaea212022-12-09 11:02:02 -07002# Copyright 2022 The ChromiumOS Authors
George Burgess IV18af5632022-08-30 14:10:53 -07003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
George Burgess IV4196b082022-11-03 17:10:47 -06005"""Populates the top-level Cargo.toml with all necessary workspace entries.
George Burgess IV18af5632022-08-30 14:10:53 -07006
George Burgess IV4196b082022-11-03 17:10:47 -06007Also updates cargo-vet's policy information with all of the crates discovered
8in the tree.
9"""
10
11import argparse
George Burgess IV18af5632022-08-30 14:10:53 -070012import logging
13import os
George Burgess IV4196b082022-11-03 17:10:47 -060014import subprocess
George Burgess IV18af5632022-08-30 14:10:53 -070015import sys
George Burgess IV4196b082022-11-03 17:10:47 -060016import textwrap
17from pathlib import Path
George Burgess IV18af5632022-08-30 14:10:53 -070018from typing import List
19
George Burgess IV4196b082022-11-03 17:10:47 -060020import toml
21
George Burgess IV18af5632022-08-30 14:10:53 -070022WORKSPACE_FILE_HEADER = """\
23# Copyright 2022 The ChromiumOS Authors.
24# Use of this source code is governed by a BSD-style license that can be
25# found in the LICENSE file.
26#
27# !! Autogenerated by `populate-workspace.py`; please don't edit. !!
28
George Burgess IV4196b082022-11-03 17:10:47 -060029[workspace.metadata.vet]
30store = { path = '../cargo-vet' }
31
George Burgess IV18af5632022-08-30 14:10:53 -070032"""
33
George Burgess IV4196b082022-11-03 17:10:47 -060034# Tools which only run on the host. These are subject to less stringent audit
35# requirements.
36HOST_TOOLS = frozenset(
37 (
38 "bindgen-deps",
39 "chromeos-dbus-bindings-deps",
40 "cxxbridge-cmd-deps",
41 "dbus-codegen-deps",
42 "factory_installer-deps",
43 "flashrom_tester-deps",
44 "prjoxide-deps",
45 "pyprjoxide-deps",
George Burgess IV18af5632022-08-30 14:10:53 -070046 )
George Burgess IV4196b082022-11-03 17:10:47 -060047)
George Burgess IV18af5632022-08-30 14:10:53 -070048
George Burgess IV4196b082022-11-03 17:10:47 -060049# Dependencies that should not be treated as crates.io dependencies.
50NON_CRATES_IO_DEPS = (
51 "deqp-runner",
52 "dlib",
53 "wayland-commons",
54 "wayland-scanner",
55 "wayland-server",
56 "wayland-sys",
57)
58
59
60def update_cargo_vet_info(
61 projects_dir: Path, projects: List[str], use_relaxed_target_criteria: bool
62):
63 """Updates cargo-vet's config.toml with requirements for all `projects`."""
64 cargo_vet_config = projects_dir.parent / "cargo-vet" / "config.toml"
65 config = cargo_vet_config.read_text(encoding="utf-8")
66
67 cargo_vet_policy = toml.loads(config).get("policy", ())
68 project_names = []
69 unseen_io_deps = set(
70 x for x in NON_CRATES_IO_DEPS if x not in cargo_vet_policy
71 )
72 for project in projects:
73 cargo_toml = projects_dir / project / "Cargo.toml"
74 with cargo_toml.open(encoding="utf-8") as f:
75 project_name = toml.load(f)["package"]["name"]
76 project_names.append((project, project_name))
77 unseen_io_deps.discard(project_name)
78
79 add_projects = [x for x in project_names if x[1] not in cargo_vet_policy]
80 add_projects += ((None, x) for x in unseen_io_deps)
81 if not add_projects:
82 logging.info("No cargo-vet-related updates to make.")
83 return
84
85 host_safety_criteria = "safe-to-run"
86 target_safety_criteria = "rule-of-two-safe-to-deploy"
87
88 # As ugly as it is to hand-update a toml file, our toml implementation has
89 # known bugs that lead to invalid toml in quite a few cases (b/242668603).
90 # Stick stuff at the end and have `cargo-vet` handle making it pretty.
91 with cargo_vet_config.open("a", encoding="utf-8") as f:
92 for project_path, project_name in add_projects:
93 if project_path:
94 logging.info(
95 "Adding entry %s from %s into cargo-vet",
96 project_path,
97 project_name,
98 )
99 else:
100 logging.info(
101 "Synthesizing entry %s for cargo-vet",
102 project_name,
103 )
104
105 if project_name in HOST_TOOLS:
106 safety_criteria = host_safety_criteria
107 note = "Only safe-to-run is required, as this is a host tool."
108 elif use_relaxed_target_criteria:
109 safety_criteria = host_safety_criteria
110 note = "Using relaxed target criteria to help with migration."
111 else:
112 safety_criteria = target_safety_criteria
113 note = None
114
115 f.write("\n")
116 f.write(
117 textwrap.dedent(
118 f"""\
119 [policy."{project_name}"]
120 criteria = ["{safety_criteria}", "crypto-safe"]
121 dev-criteria = ["{host_safety_criteria}", "crypto-safe"]
122 """
123 )
124 )
125 if note:
126 # Only handle these if we have to.
127 assert "\\" not in note and '"' not in note, note
128 f.write(f'notes = "{note}"\n')
129
130 if project_name in NON_CRATES_IO_DEPS:
131 f.write(f"audit-as-crates-io = false\n")
132
133 subprocess.check_call(
134 ["scripts/cargo-vet.py", "fmt"], cwd=projects_dir.parent
135 )
136 logging.info("Cargo vet info updated.")
137
138
139def find_projects(projects_dir: Path) -> List[str]:
140 """Returns a list of projects under `projects_dir`.
141
142 This creates src/ directories for any project that does not currently have
143 one, for convenience.
144 """
George Burgess IV18af5632022-08-30 14:10:53 -0700145 projects = []
146 for dir_path, subdirs, files in os.walk(projects_dir):
George Burgess IV7dffc252022-08-31 14:37:01 -0700147 if "Cargo.toml" not in files:
George Burgess IV18af5632022-08-30 14:10:53 -0700148 continue
149
150 dir_path = Path(dir_path)
151 if dir_path == projects_dir:
152 continue
153
154 projects.append(dir_path.relative_to(projects_dir))
155
156 # It's a waste to descend into src directories.
George Burgess IV7dffc252022-08-31 14:37:01 -0700157 if "src" in subdirs:
158 del subdirs[subdirs.index("src")]
George Burgess IV18af5632022-08-30 14:10:53 -0700159 else:
160 # ...Though if src/ doesn't exist, Cargo will get confused.
161 # Synthesize one with a nop lib.rs.
George Burgess IV7dffc252022-08-31 14:37:01 -0700162 src = dir_path / "src"
George Burgess IV18af5632022-08-30 14:10:53 -0700163 src.mkdir()
George Burgess IV7dffc252022-08-31 14:37:01 -0700164 (src / "lib.rs").write_bytes(b"")
165 logging.info("Synthesized src/lib.rs for %s", dir_path)
George Burgess IV18af5632022-08-30 14:10:53 -0700166
George Burgess IV18af5632022-08-30 14:10:53 -0700167 projects.sort()
George Burgess IV4196b082022-11-03 17:10:47 -0600168 return projects
George Burgess IV18af5632022-08-30 14:10:53 -0700169
George Burgess IV4196b082022-11-03 17:10:47 -0600170
171def get_parser() -> argparse.ArgumentParser:
172 """Gets the arg parser for this script."""
173 parser = argparse.ArgumentParser(
174 description=__doc__,
175 formatter_class=argparse.RawDescriptionHelpFormatter,
176 )
177 parser.add_argument(
178 "--use-strict-target-criteria",
179 action="store_true",
180 help="Require rule-of-two-safe-to-deploy for target crates.",
181 )
182 return parser
183
184
185def main(argv: List[str]):
186 """Main function."""
187 opts = get_parser().parse_args(argv)
188 logging.basicConfig(
189 format=">> %(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: "
190 "%(message)s",
191 level=logging.INFO,
192 )
193
194 projects_dir = Path(__file__).resolve().parent
195 projects = find_projects(projects_dir)
196 assert projects, f"No projects found under {projects_dir}"
George Burgess IV7dffc252022-08-31 14:37:01 -0700197 logging.info("Identified %d projects", len(projects))
George Burgess IV18af5632022-08-30 14:10:53 -0700198
George Burgess IV7dffc252022-08-31 14:37:01 -0700199 workspace_toml_file = projects_dir / "Cargo.toml"
200 with workspace_toml_file.open("w", encoding="utf-8") as f:
George Burgess IV18af5632022-08-30 14:10:53 -0700201 f.write(WORKSPACE_FILE_HEADER)
George Burgess IV7dffc252022-08-31 14:37:01 -0700202 # The `toml` crate writes this as a massive line, which is hard to
203 # read. Since this is simple to write, write it directly.
George Burgess IV18af5632022-08-30 14:10:53 -0700204 # TODO(b/242668603): find a toml crate with prettier formatting
George Burgess IVbdaea212022-12-09 11:02:02 -0700205 f.write("[workspace]\nmembers = [\n")
George Burgess IV18af5632022-08-30 14:10:53 -0700206 for project in projects:
207 project = str(project)
George Burgess IV7dffc252022-08-31 14:37:01 -0700208 assert '"' not in project and "\\" not in project, project
George Burgess IV18af5632022-08-30 14:10:53 -0700209 f.write(f' "{project}",\n')
George Burgess IV7dffc252022-08-31 14:37:01 -0700210 f.write("]")
George Burgess IV18af5632022-08-30 14:10:53 -0700211
George Burgess IV7dffc252022-08-31 14:37:01 -0700212 logging.info("Workspace Cargo.toml successfully written.")
George Burgess IV4196b082022-11-03 17:10:47 -0600213 update_cargo_vet_info(
214 projects_dir,
215 projects,
216 use_relaxed_target_criteria=not opts.use_strict_target_criteria,
217 )
George Burgess IV18af5632022-08-30 14:10:53 -0700218
219
George Burgess IV7dffc252022-08-31 14:37:01 -0700220if __name__ == "__main__":
George Burgess IV18af5632022-08-30 14:10:53 -0700221 sys.exit(main(sys.argv[1:]))