Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright 2016 The Chromium Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | """Builds an SDK (in a specified target directory) from the (current) source |
| 7 | repository (which should not be dirty) and a given "specification" file.""" |
| 8 | |
| 9 | |
| 10 | import argparse |
| 11 | from pylib.errors import FatalError |
| 12 | from pylib.git import Git, IsGitTreeDirty, SanityCheckGit, GitLsFiles |
| 13 | import os |
| 14 | import shutil |
| 15 | import sys |
| 16 | |
| 17 | |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 18 | def _MakeDirs(*args, **kwargs): |
| 19 | """Like |os.makedirs()|, but ignores |OSError| exceptions (it assumes that |
| 20 | these are due to the directory already existing).""" |
| 21 | try: |
| 22 | os.makedirs(*args, **kwargs) |
| 23 | except OSError: |
| 24 | pass |
| 25 | |
| 26 | |
Viet-Trung Luu | 10fc724 | 2016-03-04 15:40:04 -0800 | [diff] [blame] | 27 | def _CopyHelper(source_file, dest_file): |
| 28 | """Copies |source_file| to |dest_file|. If |source_file| is user-executable, |
| 29 | it will set |dest_file|'s mode to 0755 (user: rwx, group/other: rx); |
| 30 | otherwise, it will set it to 0644 (user: rw, group/other: r).""" |
| 31 | shutil.copy(source_file, dest_file) |
| 32 | if os.stat(source_file).st_mode & 0100: |
| 33 | os.chmod(dest_file, 0755) |
| 34 | else: |
| 35 | os.chmod(dest_file, 0644) |
| 36 | |
| 37 | |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 38 | def _CopyDir(source_path, dest_path, **kwargs): |
| 39 | """Copies directories from the source git repository (the current working |
| 40 | directory should be the root of this repository) to the destination path. |
| 41 | |source_path| and the keyword arguments are as for the arguments of |
| 42 | |GitLsFiles|. |dest_path| should be the "root" destination path. Note that a |
| 43 | file such as <source_path>/foo/bar/baz.quux is copied to |
| 44 | <dest_path>/foo/bar/baz.quux.""" |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 45 | |
| 46 | # Normalize the source path. Note that this strips any trailing '/'. |
| 47 | source_path = os.path.normpath(source_path) |
| 48 | source_files = GitLsFiles(source_path, **kwargs) |
| 49 | for source_file in source_files: |
| 50 | rel_path = source_file[len(source_path) + 1:] |
| 51 | dest_file = os.path.join(dest_path, rel_path) |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 52 | _MakeDirs(os.path.dirname(dest_file)) |
Viet-Trung Luu | 10fc724 | 2016-03-04 15:40:04 -0800 | [diff] [blame] | 53 | _CopyHelper(source_file, dest_file) |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 54 | |
| 55 | |
| 56 | def _CopyFiles(source_files, dest_path): |
| 57 | """Copies a given source file or files from the source git repository to the |
| 58 | given destination path (the current working directory should be the root of |
| 59 | this repository) |source_files| should either be a relative path to a single |
| 60 | file in the source git repository or an iterable of such paths; note that this |
| 61 | does not check that files are actually in the git repository (i.e., are |
| 62 | tracked).""" |
| 63 | |
| 64 | if type(source_files) is str: |
| 65 | source_files = [source_files] |
| 66 | _MakeDirs(dest_path) |
| 67 | for source_file in source_files: |
Viet-Trung Luu | 10fc724 | 2016-03-04 15:40:04 -0800 | [diff] [blame] | 68 | _CopyHelper(source_file, |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 69 | os.path.join(dest_path, os.path.basename(source_file))) |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 70 | |
| 71 | |
Viet-Trung Luu | 22675d7 | 2016-02-25 15:47:12 -0800 | [diff] [blame] | 72 | def _GitGetRevision(): |
| 73 | """Returns the HEAD revision of the source git repository (the current working |
| 74 | directory should be the root of this repository).""" |
| 75 | |
| 76 | return Git("rev-parse", "HEAD").strip() |
| 77 | |
| 78 | |
| 79 | def _ReadFile(source_file): |
| 80 | """Reads the specified file from the source git repository (note: It does not |
| 81 | verify that |source_file| is actually in the git repository, i.e., is tracked) |
| 82 | and returns its contents.""" |
| 83 | |
| 84 | with open(source_file, "rb") as f: |
| 85 | return f.read() |
| 86 | |
| 87 | |
| 88 | def _WriteFile(dest_file, contents): |
| 89 | """Writes the given contents to the specified destination file.""" |
| 90 | |
| 91 | _MakeDirs(os.path.dirname(dest_file)) |
| 92 | with open(dest_file, "wb") as f: |
| 93 | return f.write(contents) |
| 94 | |
| 95 | |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 96 | def main(): |
| 97 | parser = argparse.ArgumentParser( |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 98 | description="Constructs an SDK from a specification.") |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 99 | parser.add_argument("--allow-dirty-tree", dest="allow_dirty_tree", |
| 100 | action="store_true", |
| 101 | help="proceed even if the source tree is dirty") |
| 102 | parser.add_argument("sdk_spec_file", metavar="sdk_spec_file.sdk", |
| 103 | type=argparse.FileType("rb"), |
| 104 | help="spec file for the SDK to build") |
| 105 | parser.add_argument("target_dir", |
| 106 | help="target directory (must not already exist)") |
| 107 | args = parser.parse_args() |
| 108 | |
| 109 | target_dir = os.path.abspath(args.target_dir) |
| 110 | |
| 111 | # CD to the "src" directory (we should currently be in src/sdk_build). |
| 112 | src_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) |
| 113 | os.chdir(src_dir) |
| 114 | |
| 115 | SanityCheckGit() |
| 116 | |
| 117 | if not args.allow_dirty_tree and IsGitTreeDirty(): |
| 118 | FatalError("tree appears to be dirty") |
| 119 | |
Viet-Trung Luu | 10fc724 | 2016-03-04 15:40:04 -0800 | [diff] [blame] | 120 | # Set the umask, so that files/directories we create will be world- (and |
| 121 | # group-) readable (and executable, for directories). |
| 122 | os.umask(0022) |
| 123 | |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 124 | try: |
| 125 | os.mkdir(target_dir) |
| 126 | except OSError: |
| 127 | FatalError("failed to create target directory %s" % target_dir) |
| 128 | |
Viet-Trung Luu | 22675d7 | 2016-02-25 15:47:12 -0800 | [diff] [blame] | 129 | # Wrappers to actually write things to the target directory: |
| 130 | |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 131 | def CopyDirToTargetDir(source_path, rel_dest_path, **kwargs): |
| 132 | return _CopyDir(source_path, os.path.join(target_dir, rel_dest_path), |
| 133 | **kwargs) |
| 134 | |
| 135 | def CopyFilesToTargetDir(source_files, rel_dest_path, **kwargs): |
| 136 | return _CopyFiles(source_files, os.path.join(target_dir, rel_dest_path), |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 137 | **kwargs) |
| 138 | |
Viet-Trung Luu | 22675d7 | 2016-02-25 15:47:12 -0800 | [diff] [blame] | 139 | def WriteFileToTargetDir(rel_dest_file, contents): |
| 140 | return _WriteFile(os.path.join(target_dir, rel_dest_file), contents) |
| 141 | |
Viet-Trung Luu | f386354 | 2016-02-19 14:19:36 -0800 | [diff] [blame] | 142 | execution_globals = {"CopyDir": CopyDirToTargetDir, |
| 143 | "CopyFiles": CopyFilesToTargetDir, |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 144 | "FatalError": FatalError, |
Viet-Trung Luu | 22675d7 | 2016-02-25 15:47:12 -0800 | [diff] [blame] | 145 | "GitGetRevision": _GitGetRevision, |
| 146 | "GitLsFiles": GitLsFiles, |
| 147 | "ReadFile": _ReadFile, |
| 148 | "WriteFile": WriteFileToTargetDir} |
Viet-Trung Luu | a0bce06 | 2016-02-17 13:29:12 -0800 | [diff] [blame] | 149 | exec args.sdk_spec_file in execution_globals |
| 150 | |
| 151 | return 0 |
| 152 | |
| 153 | |
| 154 | if __name__ == "__main__": |
| 155 | sys.exit(main()) |