Skip to content

Commit 2122d87

Browse files
authored
firestore/scripts/cp_unity_dlls.py added (#651)
1 parent 93bd2e9 commit 2122d87

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

firestore/scripts/cp_unity_dlls.py

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#!/usr/bin/env python
2+
"""
3+
%s <src_dir> <dest_dir> [dest_dir2, [dest_dir3, ...]] [flags]
4+
5+
Copies the macOS build artifact of the Firebase Unity SDK to the directory of
6+
a Unity application.
7+
8+
src_dir
9+
The directory into which the build artifacts of the Firebase Unity SDK were
10+
written. This is the value that was specified to the -B argument of cmake.
11+
12+
dest_dir
13+
The directories of the Unity applications into which to deploy the build
14+
artifacts.
15+
"""
16+
17+
from collections.abc import Sequence
18+
import abc
19+
import enum
20+
import fnmatch
21+
import pathlib
22+
import re
23+
import shutil
24+
from typing import Optional
25+
26+
from absl import app
27+
from absl import flags
28+
from absl import logging
29+
30+
31+
FLAG_FILTER = flags.DEFINE_string(
32+
name="filter",
33+
default=None,
34+
help="The names of the files to include when copying; only the files whose "
35+
"names match this filter will be copied. The pattern uses Python's "
36+
"fnmatch module, which recognizes *, ?, and [anychar]",
37+
)
38+
39+
FLAG_DRY_RUN = flags.DEFINE_boolean(
40+
name="dry_run",
41+
default=False,
42+
help="Log what file operations would be performed, without actually "
43+
"performing them.",
44+
)
45+
46+
47+
def main(argv: Sequence[str]) -> None:
48+
if len(argv) < 2:
49+
raise app.UsageError("src_dir must be specified")
50+
elif len(argv) < 3:
51+
raise app.UsageError("at least one dest_dir must be specified")
52+
53+
src_dir = pathlib.Path(argv[1])
54+
dest_dirs = tuple(pathlib.Path(x) for x in argv[2:])
55+
56+
if FLAG_FILTER.value:
57+
copy_pattern = re.compile(fnmatch.translate(FLAG_FILTER.value))
58+
else:
59+
copy_pattern = None
60+
61+
if FLAG_DRY_RUN.value:
62+
fs = NoOpFileSystem()
63+
else:
64+
fs = RealFileSystem()
65+
66+
for dest_dir in dest_dirs:
67+
copier = BuildArtifactCopier(src_dir, dest_dir, fs, copy_pattern)
68+
copier.run()
69+
70+
71+
@enum.unique
72+
class OS(enum.Enum):
73+
MAC = (".bundle")
74+
LINUX = (".so")
75+
76+
def __init__(self, lib_filename_suffix: str) -> None:
77+
self.lib_filename_suffix = lib_filename_suffix
78+
79+
80+
class FileSystem(abc.ABC):
81+
82+
@abc.abstractmethod
83+
def unlink(self, path: pathlib.Path) -> None:
84+
raise NotImplementedError()
85+
86+
@abc.abstractmethod
87+
def copy_file(self, src: pathlib.Path, dest: pathlib.Path) -> None:
88+
raise NotImplementedError()
89+
90+
91+
class RealFileSystem(FileSystem):
92+
93+
def unlink(self, path: pathlib.Path) -> None:
94+
path.unlink()
95+
96+
def copy_file(self, src: pathlib.Path, dest: pathlib.Path) -> None:
97+
shutil.copy2(src, dest)
98+
99+
100+
class NoOpFileSystem(FileSystem):
101+
102+
def unlink(self, path: pathlib.Path) -> None:
103+
pass
104+
105+
def copy_file(self, src: pathlib.Path, dest: pathlib.Path) -> None:
106+
pass
107+
108+
109+
class BuildArtifactCopier:
110+
111+
def __init__(
112+
self,
113+
src_dir: pathlib.Path,
114+
dest_dir: pathlib.Path,
115+
fs: FileSystem,
116+
copy_pattern: Optional[re.Pattern],
117+
) -> None:
118+
self.src_dir = src_dir
119+
self.dest_dir = dest_dir
120+
self.fs = fs
121+
self.copy_pattern = copy_pattern
122+
123+
def run(self) -> None:
124+
logging.info("Copying macOS build artifacts from %s to %s", self.src_dir, self.dest_dir)
125+
(src_cpp_app_library_os, src_cpp_app_library_file) = self.find_src_cpp_app_library()
126+
self.cleanup_old_dlls(src_cpp_app_library_os, src_cpp_app_library_file.name)
127+
self.copy_files(src_cpp_app_library_os, src_cpp_app_library_file)
128+
129+
def find_src_cpp_app_library(self) -> tuple[OS, pathlib.Path]:
130+
found_artifacts: list[tuple[OS, pathlib.Path]] = []
131+
132+
logging.info("Searching for C++ library build artifacts in %s", self.src_dir)
133+
for candidate_artifact_path in self.src_dir.iterdir():
134+
for candidate_os in OS:
135+
if candidate_artifact_path.name.endswith(candidate_os.lib_filename_suffix):
136+
logging.info("Found C++ library build artifact: %s", candidate_artifact_path)
137+
found_artifacts.append((candidate_os, candidate_artifact_path))
138+
139+
if len(found_artifacts) == 0:
140+
raise FirebaseCppLibraryBuildArtifactNotFound(
141+
f"No C++ library build artifacts found in {self.src_dir}; "
142+
f"expected to find exactly one file whose name has one of the "
143+
f"following suffixes: " + ", ".join(OS)
144+
)
145+
elif len(found_artifacts) > 1:
146+
raise FirebaseCppLibraryBuildArtifactNotFound(
147+
f"Found {len(found_artifacts)} C++ library build artifacts in "
148+
f"{self.src_dir}; "
149+
f"expected to find exactly one file whose name has one of the "
150+
f"following suffixes: " + ", ".join(OS)
151+
)
152+
153+
return found_artifacts[0]
154+
155+
def cleanup_old_dlls(self, os: OS, src_cpp_app_library_file_name: str) -> None:
156+
if self.copy_pattern is not None and not self.copy_pattern.fullmatch(src_cpp_app_library_file_name):
157+
return
158+
159+
expr = re.compile(r"FirebaseCppApp-\d+_\d+_\d+" + re.escape(os.lib_filename_suffix))
160+
if not expr.fullmatch(src_cpp_app_library_file_name):
161+
raise ValueError(f"invalid filename: {src_cpp_app_library_file_name}")
162+
163+
x86_64_dir = self.dest_dir / "Assets" / "Firebase" / "Plugins" / "x86_64"
164+
for candidate_dll_file in x86_64_dir.iterdir():
165+
if expr.fullmatch(candidate_dll_file.name):
166+
if candidate_dll_file.name == src_cpp_app_library_file_name:
167+
continue
168+
logging.info("Deleting %s", candidate_dll_file)
169+
self.fs.unlink(candidate_dll_file)
170+
candidate_dll_meta_file = candidate_dll_file.parent / (candidate_dll_file.name + ".meta")
171+
if candidate_dll_meta_file.exists():
172+
logging.info("Deleting %s", candidate_dll_meta_file)
173+
self.fs.unlink(candidate_dll_meta_file)
174+
175+
def copy_files(self, os: OS, src_cpp_app_library_file: pathlib.Path) -> None:
176+
dest_file_str_by_src_file_str = {
177+
"app/obj/x86/Release/Firebase.App.dll": "Assets/Firebase/Plugins/Firebase.App.dll",
178+
"app/platform/obj/x86/Release/Firebase.Platform.dll": "Assets/Firebase/Plugins/Firebase.Platform.dll",
179+
"app/task_extension/obj/x86/Release/Firebase.TaskExtension.dll": "Assets/Firebase/Plugins/Firebase.TaskExtension.dll",
180+
"auth/obj/x86/Release/Firebase.Auth.dll": "Assets/Firebase/Plugins/Firebase.Auth.dll",
181+
"firestore/obj/x86/Release/Firebase.Firestore.dll": "Assets/Firebase/Plugins/Firebase.Firestore.dll",
182+
"firestore/FirebaseCppFirestore" + os.lib_filename_suffix: "Assets/Firebase/Plugins/x86_64/FirebaseCppFirestore" + os.lib_filename_suffix,
183+
"auth/FirebaseCppAuth" + os.lib_filename_suffix: "Assets/Firebase/Plugins/x86_64/FirebaseCppAuth" + os.lib_filename_suffix,
184+
}
185+
186+
dest_path_by_src_path = {
187+
self.src_dir / src_file_str: self.dest_dir / dest_file_str
188+
for (src_file_str, dest_file_str)
189+
in dest_file_str_by_src_file_str.items()
190+
}
191+
dest_path_by_src_path[src_cpp_app_library_file] = (
192+
self.dest_dir / "Assets" / "Firebase" / "Plugins"
193+
/ "x86_64" / src_cpp_app_library_file.name
194+
)
195+
196+
for (src_file, dest_file) in sorted(dest_path_by_src_path.items()):
197+
if self.copy_pattern is None or self.copy_pattern.fullmatch(src_file.name):
198+
logging.info("Copying %s to %s", src_file, dest_file)
199+
self.fs.copy_file(src_file, dest_file)
200+
201+
202+
class FirebaseCppLibraryBuildArtifactNotFound(Exception):
203+
pass
204+
205+
206+
if __name__ == "__main__":
207+
app.run(main)

0 commit comments

Comments
 (0)