xref: /aosp_15_r20/external/pigweed/pw_build/py/pw_build/copy_from_cipd.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2021 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"""Copies files from CIPD to a specified directory.
15
16By default, Pigweed installs packages from a manifest file to a CIPD
17subdirectory as part of environment setup. This script will copy files from this
18directory into a specified output directory.
19
20Here's an example of how to use this script:
21
22Let's say you have a package with a static library:
23
24CIPD path: `pigweed/third_party/libsomething`
25Files:
26  ./libsomething/include/something.h
27  ./libsomething/libsomething.a
28
29And this package was referenced in my_project_packages.json, which was provided
30as a --cipd-package-file in your bootstrap script.
31
32To copy the static libraryto $PW_PROJECT_ROOT/static_libraries, you'd have an
33invocation something like this:
34
35copy_from_cipd --package-name=pigweed/third_party/libsomething \
36               --mainfest=$PW_PROJECT_ROOT/tools/my_project_packages.json \
37               --file=libsomething/libsomething.a \
38               --out=$PW_PROJECT_ROOT/static_libraries
39"""
40
41import argparse
42import json
43import logging
44import os
45import shutil
46import subprocess
47import sys
48from pathlib import Path
49
50import pw_env_setup.cipd_setup.update
51
52logger = logging.getLogger(__name__)
53
54
55def parse_args():
56    """Parse arguments."""
57    parser = argparse.ArgumentParser(description=__doc__)
58    parser.add_argument(
59        '--verbose', '-v', help='Verbose output', action='store_true'
60    )
61    parser.add_argument(
62        '--manifest',
63        required=True,
64        type=Path,
65        help='Path to CIPD JSON manifest file',
66    )
67    parser.add_argument(
68        '--out-dir',
69        type=Path,
70        default='.',
71        help='Output folder to copy the specified file to',
72    )
73    parser.add_argument(
74        '--package-name', required=True, help='The CIPD package name'
75    )
76    # TODO(pwbug/334) Support multiple values for --file.
77    parser.add_argument(
78        '--file',
79        required=True,
80        type=Path,
81        help='Path of the file to copy from the CIPD package. '
82        'This is relative to the CIPD package root of the '
83        'provided manifest.',
84    )
85    parser.add_argument(
86        '--cipd-package-root',
87        type=Path,
88        help="Path to the root of the package's install "
89        'directory. This is usually at '
90        'PW_{manifest name}_CIPD_INSTALL_DIR',
91    )
92    return parser.parse_args()
93
94
95def check_version(manifest, cipd_path, package_name):
96    base_package_name = os.path.basename(package_name)
97    instance_id_path = os.path.join(
98        cipd_path, '.versions', f'{base_package_name}.cipd_version'
99    )
100    with open(instance_id_path, 'r') as ins:
101        instance_id = json.load(ins)['instance_id']
102
103    with open(manifest, 'r') as ins:
104        data = json.load(ins)['packages']
105
106    path = None
107    expected_version = None
108    for entry in data:
109        if package_name in entry['path']:
110            path = entry['path']
111            expected_version = entry['tags'][0]
112    if not path:
113        raise LookupError(f'failed to find {package_name} entry')
114
115    cmd = ['cipd', 'describe', path, '-version', instance_id]
116    output = subprocess.check_output(cmd).decode()
117    if expected_version not in output:
118        pw_env_setup.cipd_setup.update.update(
119            'cipd',
120            (manifest,),
121            os.environ['PW_CIPD_INSTALL_DIR'],
122            os.environ['CIPD_CACHE_DIR'],
123        )
124
125
126def main():
127    args = parse_args()
128
129    if args.verbose:
130        logger.setLevel(logging.DEBUG)
131
132    # Try to infer CIPD install root from the manifest name.
133    if args.cipd_package_root is None:
134        file_base_name = args.manifest.stem
135        args.cipd_var = 'PW_{}_CIPD_INSTALL_DIR'.format(file_base_name.upper())
136        try:
137            args.cipd_package_root = os.environ[args.cipd_var]
138        except KeyError:
139            logger.error(
140                "The %s environment variable isn't set. Did you forget to run "
141                '`. ./bootstrap.sh`? Is the %s manifest installed to a '
142                'different path?',
143                args.cipd_var,
144                file_base_name,
145            )
146            sys.exit(1)
147
148    check_version(args.manifest, args.cipd_package_root, args.package_name)
149
150    shutil.copyfile(
151        os.path.join(args.cipd_package_root, args.file),
152        os.path.join(args.out_dir, args.file),
153    )
154
155
156if __name__ == '__main__':
157    logging.basicConfig()
158    main()
159