xref: /aosp_15_r20/external/pigweed/pw_build/py/pw_build/create_gn_venv.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2022 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Crate a venv."""
15
16import argparse
17import os
18import pathlib
19import platform
20import shutil
21import stat
22import sys
23import venv
24
25
26def _parse_args() -> argparse.Namespace:
27    parser = argparse.ArgumentParser(description=__doc__)
28    parser.add_argument(
29        '--depfile',
30        type=pathlib.Path,
31        required=True,
32        help='Path at which a depfile should be written.',
33    )
34    parser.add_argument(
35        '--destination-dir',
36        type=pathlib.Path,
37        required=True,
38        help='Path to venv directory.',
39    )
40    parser.add_argument(
41        '--stampfile',
42        type=pathlib.Path,
43        required=True,
44        help="Path to this target's stamp file.",
45    )
46    return parser.parse_args()
47
48
49def _rm_dir(path_to_delete: pathlib.Path) -> None:
50    """Delete a directory recursively.
51
52    On Windows if a file can't be deleted, mark it as writable then delete.
53    """
54
55    def make_writable_and_delete(_func, path, _exc_info):
56        os.chmod(path, stat.S_IWRITE)
57        os.unlink(path)
58
59    on_rm_error = None
60    if platform.system() == 'Windows':
61        on_rm_error = make_writable_and_delete
62    shutil.rmtree(path_to_delete, onerror=on_rm_error)
63
64
65def main(
66    depfile: pathlib.Path,
67    destination_dir: pathlib.Path,
68    stampfile: pathlib.Path,
69) -> None:
70    # Create the virtualenv.
71    if destination_dir.exists():
72        _rm_dir(destination_dir)
73    venv.create(destination_dir, symlinks=True, with_pip=True)
74
75    # Write out the depfile, making sure the Python path is
76    # relative to the outdir so that this doesn't add user-specific
77    # info to the build.
78    out_dir_path = pathlib.Path(os.getcwd()).resolve()
79    python_path = pathlib.Path(sys.executable).resolve()
80    rel_python_path = os.path.relpath(python_path, start=out_dir_path)
81    depfile.write_text(f'{stampfile}: \\\n  {rel_python_path}')
82
83
84if __name__ == '__main__':
85    main(**vars(_parse_args()))
86