blob: 38142c6169dc9e18f14b2032b3612abb09f8a118 [file] [log] [blame]
(raulenrique)dfdda472018-06-04 12:02:29 -07001# Copyright (C) 2018 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://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,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Tool functions to deal with files."""
15
16import datetime
Dan Albert308130a2023-04-12 23:07:23 +000017from functools import cache
(raulenrique)dfdda472018-06-04 12:02:29 -070018import os
Haibo Huang329e6812020-05-29 14:12:20 -070019from pathlib import Path
Haibo Huang27d2e0b2021-02-10 15:20:19 -080020import textwrap
(raulenrique)dfdda472018-06-04 12:02:29 -070021
Haibo Huang329e6812020-05-29 14:12:20 -070022# pylint: disable=import-error
23from google.protobuf import text_format # type: ignore
(raulenrique)dfdda472018-06-04 12:02:29 -070024
Haibo Huang329e6812020-05-29 14:12:20 -070025# pylint: disable=import-error
26import metadata_pb2 # type: ignore
(raulenrique)dfdda472018-06-04 12:02:29 -070027
Dan Albert04861872023-04-07 21:15:47 +000028
(raulenrique)dfdda472018-06-04 12:02:29 -070029METADATA_FILENAME = 'METADATA'
30
31
Dan Albert308130a2023-04-12 23:07:23 +000032@cache
33def external_path() -> Path:
34 """Returns the path to //external.
35
36 We cannot use the relative path from this file to find the top of the tree because
37 this will often be run in a "compiled" form from an arbitrary location in the out
38 directory. We can't fully rely on ANDROID_BUILD_TOP because not all contexts will
39 have run envsetup/lunch either. We use ANDROID_BUILD_TOP whenever it is set, but if
40 it is not set we instead rely on the convention that the CWD is the root of the tree
41 (updater.sh will cd there before executing).
42
43 There is one other context where this function cannot succeed: CI. Tests run in CI
44 do not have a source tree to find, so calling this function in that context will
45 fail.
46 """
47 android_top = Path(os.environ.get("ANDROID_BUILD_TOP", os.getcwd()))
48 top = android_top / 'external'
49
50 if not top.exists():
51 raise RuntimeError(
52 f"{top} does not exist. This program must be run from the "
53 f"root of an Android tree (CWD is {os.getcwd()})."
54 )
55 return top
56
57
Haibo Huanga08fb602020-05-29 16:24:13 -070058def get_absolute_project_path(proj_path: Path) -> Path:
(raulenrique)dfdda472018-06-04 12:02:29 -070059 """Gets absolute path of a project.
60
61 Path resolution starts from external/.
62 """
Dan Albert308130a2023-04-12 23:07:23 +000063 return external_path() / proj_path
(raulenrique)dfdda472018-06-04 12:02:29 -070064
65
Haibo Huanga08fb602020-05-29 16:24:13 -070066def get_metadata_path(proj_path: Path) -> Path:
(raulenrique)dfdda472018-06-04 12:02:29 -070067 """Gets the absolute path of METADATA for a project."""
Haibo Huanga08fb602020-05-29 16:24:13 -070068 return get_absolute_project_path(proj_path) / METADATA_FILENAME
(raulenrique)dfdda472018-06-04 12:02:29 -070069
70
Haibo Huanga08fb602020-05-29 16:24:13 -070071def get_relative_project_path(proj_path: Path) -> Path:
(raulenrique)dfdda472018-06-04 12:02:29 -070072 """Gets the relative path of a project starting from external/."""
Dan Albert308130a2023-04-12 23:07:23 +000073 return get_absolute_project_path(proj_path).relative_to(external_path())
(raulenrique)dfdda472018-06-04 12:02:29 -070074
75
Dan Albert25ea5922023-03-23 22:32:54 +000076def canonicalize_project_path(proj_path: Path) -> Path:
77 """Returns the canonical representation of the project path.
78
79 For paths that are in the same tree as external_updater (the common case), the
80 canonical path is the path of the project relative to //external.
81
82 For paths that are in a different tree (an uncommon case used for updating projects
83 in other builds such as the NDK), the canonical path is the absolute path.
84 """
85 try:
86 return get_relative_project_path(proj_path)
87 except ValueError:
88 # A less common use case, but the path might be to a non-local tree, in which case
89 # the path will not be relative to our tree. This happens when using
90 # external_updater in another project like the NDK or rr.
91 if proj_path.is_absolute():
92 return proj_path
93
94 # Not relative to //external, and not an absolute path. This case hasn't existed
95 # before, so it has no canonical form.
96 raise ValueError(
Dan Albert308130a2023-04-12 23:07:23 +000097 f"{proj_path} must be either an absolute path or relative to {external_path()}"
Dan Albert25ea5922023-03-23 22:32:54 +000098 )
99
100
Haibo Huanga08fb602020-05-29 16:24:13 -0700101def read_metadata(proj_path: Path) -> metadata_pb2.MetaData:
(raulenrique)dfdda472018-06-04 12:02:29 -0700102 """Reads and parses METADATA file for a project.
103
104 Args:
105 proj_path: Path to the project.
106
107 Returns:
108 Parsed MetaData proto.
109
110 Raises:
111 text_format.ParseError: Occurred when the METADATA file is invalid.
112 FileNotFoundError: Occurred when METADATA file is not found.
113 """
114
Haibo Huang329e6812020-05-29 14:12:20 -0700115 with get_metadata_path(proj_path).open('r') as metadata_file:
(raulenrique)dfdda472018-06-04 12:02:29 -0700116 metadata = metadata_file.read()
117 return text_format.Parse(metadata, metadata_pb2.MetaData())
118
119
Chih-Hung Hsiehdd1915d2020-09-29 14:22:12 -0700120def write_metadata(proj_path: Path, metadata: metadata_pb2.MetaData, keep_date: bool) -> None:
(raulenrique)dfdda472018-06-04 12:02:29 -0700121 """Writes updated METADATA file for a project.
122
123 This function updates last_upgrade_date in metadata and write to the project
124 directory.
125
126 Args:
127 proj_path: Path to the project.
128 metadata: The MetaData proto to write.
Chih-Hung Hsiehdd1915d2020-09-29 14:22:12 -0700129 keep_date: Do not change date.
(raulenrique)dfdda472018-06-04 12:02:29 -0700130 """
131
Chih-Hung Hsiehdd1915d2020-09-29 14:22:12 -0700132 if not keep_date:
133 date = metadata.third_party.last_upgrade_date
134 now = datetime.datetime.now()
135 date.year = now.year
136 date.month = now.month
137 date.day = now.day
Dan Albert98dfa742023-03-23 23:07:27 +0000138 try:
Dan Albert12e45e42023-03-29 21:50:05 +0000139 rel_proj_path = str(get_relative_project_path(proj_path))
Dan Albert98dfa742023-03-23 23:07:27 +0000140 except ValueError:
141 # Absolute paths to other trees will not be relative to our tree. There are
142 # not portable instructions for upgrading that project, since the path will
143 # differ between machines (or checkouts).
144 rel_proj_path = "<absolute path to project>"
Sadaf Ebrahimi47d0bac2022-10-21 17:47:06 +0000145 usage_hint = textwrap.dedent(f"""\
146 # This project was upgraded with external_updater.
Sadaf Ebrahimi4f27e4c2022-11-02 16:34:03 +0000147 # Usage: tools/external_updater/updater.sh update {rel_proj_path}
Sadaf Ebrahimi47d0bac2022-10-21 17:47:06 +0000148 # For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
149
150 """)
151 text_metadata = usage_hint + text_format.MessageToString(metadata)
Haibo Huang329e6812020-05-29 14:12:20 -0700152 with get_metadata_path(proj_path).open('w') as metadata_file:
Haibo Huang27d2e0b2021-02-10 15:20:19 -0800153 if metadata.third_party.license_type == metadata_pb2.LicenseType.BY_EXCEPTION_ONLY:
154 metadata_file.write(textwrap.dedent("""\
Sadaf Ebrahimi47d0bac2022-10-21 17:47:06 +0000155 # THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS. PLEASE
156 # CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE
157 # DEPENDING ON IT IN YOUR PROJECT.
158
Haibo Huang27d2e0b2021-02-10 15:20:19 -0800159 """))
(raulenrique)dfdda472018-06-04 12:02:29 -0700160 metadata_file.write(text_metadata)