SDL

Форк
0
/
create-android-project.py 
238 строк · 8.6 Кб
1
#!/usr/bin/env python3
2
import os
3
from argparse import ArgumentParser
4
from pathlib import Path
5
import re
6
import shutil
7
import sys
8
import textwrap
9

10

11
SDL_ROOT = Path(__file__).resolve().parents[1]
12

13
def extract_sdl_version() -> str:
14
    """
15
    Extract SDL version from SDL3/SDL_version.h
16
    """
17

18
    with open(SDL_ROOT / "include/SDL3/SDL_version.h") as f:
19
        data = f.read()
20

21
    major = int(next(re.finditer(r"#define\s+SDL_MAJOR_VERSION\s+([0-9]+)", data)).group(1))
22
    minor = int(next(re.finditer(r"#define\s+SDL_MINOR_VERSION\s+([0-9]+)", data)).group(1))
23
    micro = int(next(re.finditer(r"#define\s+SDL_MICRO_VERSION\s+([0-9]+)", data)).group(1))
24
    return f"{major}.{minor}.{micro}"
25

26
def replace_in_file(path: Path, regex_what: str, replace_with: str) -> None:
27
    with path.open("r") as f:
28
        data = f.read()
29

30
    new_data, count = re.subn(regex_what, replace_with, data)
31

32
    assert count > 0, f"\"{regex_what}\" did not match anything in \"{path}\""
33

34
    with open(path, "w") as f:
35
        f.write(new_data)
36

37

38
def android_mk_use_prefab(path: Path) -> None:
39
    """
40
    Replace relative SDL inclusion with dependency on prefab package
41
    """
42

43
    with path.open() as f:
44
        data = "".join(line for line in f.readlines() if "# SDL" not in line)
45

46
    data, _ = re.subn("[\n]{3,}", "\n\n", data)
47

48
    newdata = data + textwrap.dedent("""
49
        # https://google.github.io/prefab/build-systems.html
50

51
        # Add the prefab modules to the import path.
52
        $(call import-add-path,/out)
53

54
        # Import SDL3 so we can depend on it.
55
        $(call import-module,prefab/SDL3)
56
    """)
57

58
    with path.open("w") as f:
59
        f.write(newdata)
60

61

62
def cmake_mk_no_sdl(path: Path) -> None:
63
    """
64
    Don't add the source directories of SDL/SDL_image/SDL_mixer/...
65
    """
66

67
    with path.open() as f:
68
        lines = f.readlines()
69

70
    newlines: list[str] = []
71
    for line in lines:
72
        if "add_subdirectory(SDL" in line:
73
            while newlines[-1].startswith("#"):
74
                newlines = newlines[:-1]
75
            continue
76
        newlines.append(line)
77

78
    newdata, _ = re.subn("[\n]{3,}", "\n\n", "".join(newlines))
79

80
    with path.open("w") as f:
81
        f.write(newdata)
82

83

84
def gradle_add_prefab_and_aar(path: Path, aar: str) -> None:
85
    with path.open() as f:
86
        data = f.read()
87

88
    data, count = re.subn("android {", textwrap.dedent("""
89
        android {
90
            buildFeatures {
91
                prefab true
92
            }"""), data)
93
    assert count == 1
94

95
    data, count = re.subn("dependencies {", textwrap.dedent(f"""
96
        dependencies {{
97
            implementation files('libs/{aar}')"""), data)
98
    assert count == 1
99

100
    with path.open("w") as f:
101
        f.write(data)
102

103

104
def gradle_add_package_name(path: Path, package_name: str) -> None:
105
    with path.open() as f:
106
        data = f.read()
107

108
    data, count = re.subn("org.libsdl.app", package_name, data)
109
    assert count >= 1
110

111
    with path.open("w") as f:
112
        f.write(data)
113

114

115
def main() -> int:
116
    description = "Create a simple Android gradle project from input sources."
117
    epilog = textwrap.dedent("""\
118
        You need to manually copy a prebuilt SDL3 Android archive into the project tree when using the aar variant.
119
        
120
        Any changes you have done to the sources in the Android project will be lost
121
    """)
122
    parser = ArgumentParser(description=description, epilog=epilog, allow_abbrev=False)
123
    parser.add_argument("package_name", metavar="PACKAGENAME", help="Android package name (e.g. com.yourcompany.yourapp)")
124
    parser.add_argument("sources", metavar="SOURCE", nargs="*", help="Source code of your application. The files are copied to the output directory.")
125
    parser.add_argument("--variant", choices=["copy", "symlink", "aar"], default="copy", help="Choose variant of SDL project (copy: copy SDL sources, symlink: symlink SDL sources, aar: use Android aar archive)")
126
    parser.add_argument("--output", "-o", default=SDL_ROOT / "build", type=Path, help="Location where to store the Android project")
127
    parser.add_argument("--version", default=None, help="SDL3 version to use as aar dependency (only used for aar variant)")
128

129
    args = parser.parse_args()
130
    if not args.sources:
131
        print("Reading source file paths from stdin (press CTRL+D to stop)")
132
        args.sources = [path for path in sys.stdin.read().strip().split() if path]
133
    if not args.sources:
134
        parser.error("No sources passed")
135

136
    if not os.getenv("ANDROID_HOME"):
137
        print("WARNING: ANDROID_HOME environment variable not set", file=sys.stderr)
138
    if not os.getenv("ANDROID_NDK_HOME"):
139
        print("WARNING: ANDROID_NDK_HOME environment variable not set", file=sys.stderr)
140

141
    args.sources = [Path(src) for src in args.sources]
142

143
    build_path = args.output / args.package_name
144

145
    # Remove the destination folder
146
    shutil.rmtree(build_path, ignore_errors=True)
147

148
    # Copy the Android project
149
    shutil.copytree(SDL_ROOT / "android-project", build_path)
150

151
    # Add the source files to the ndk-build and cmake projects
152
    replace_in_file(build_path / "app/jni/src/Android.mk", r"YourSourceHere\.c", " \\\n    ".join(src.name for src in args.sources))
153
    replace_in_file(build_path / "app/jni/src/CMakeLists.txt", r"YourSourceHere\.c", "\n    ".join(src.name for src in args.sources))
154

155
    # Remove placeholder source "YourSourceHere.c"
156
    (build_path / "app/jni/src/YourSourceHere.c").unlink()
157

158
    # Copy sources to output folder
159
    for src in args.sources:
160
        if not src.is_file():
161
            parser.error(f"\"{src}\" is not a file")
162
        shutil.copyfile(src, build_path / "app/jni/src" / src.name)
163

164
    sdl_project_files = (
165
        SDL_ROOT / "src",
166
        SDL_ROOT / "include",
167
        SDL_ROOT / "LICENSE.txt",
168
        SDL_ROOT / "README.md",
169
        SDL_ROOT / "Android.mk",
170
        SDL_ROOT / "CMakeLists.txt",
171
        SDL_ROOT / "cmake",
172
    )
173
    if args.variant == "copy":
174
        (build_path / "app/jni/SDL").mkdir(exist_ok=True, parents=True)
175
        for sdl_project_file in sdl_project_files:
176
            # Copy SDL project files and directories
177
            if sdl_project_file.is_dir():
178
                shutil.copytree(sdl_project_file, build_path / "app/jni/SDL" / sdl_project_file.name)
179
            elif sdl_project_file.is_file():
180
                shutil.copyfile(sdl_project_file, build_path / "app/jni/SDL" / sdl_project_file.name)
181
    elif args.variant == "symlink":
182
        (build_path / "app/jni/SDL").mkdir(exist_ok=True, parents=True)
183
        # Create symbolic links for all SDL project files
184
        for sdl_project_file in sdl_project_files:
185
            os.symlink(sdl_project_file, build_path / "app/jni/SDL" / sdl_project_file.name)
186
    elif args.variant == "aar":
187
        if not args.version:
188
            args.version = extract_sdl_version()
189

190
        major = args.version.split(".")[0]
191
        aar = f"SDL{ major }-{ args.version }.aar"
192

193
        # Remove all SDL java classes
194
        shutil.rmtree(build_path / "app/src/main/java")
195

196
        # Use prefab to generate include-able files
197
        gradle_add_prefab_and_aar(build_path / "app/build.gradle", aar=aar)
198

199
        # Make sure to use the prefab-generated files and not SDL sources
200
        android_mk_use_prefab(build_path / "app/jni/src/Android.mk")
201
        cmake_mk_no_sdl(build_path / "app/jni/CMakeLists.txt")
202

203
        aar_libs_folder = build_path / "app/libs"
204
        aar_libs_folder.mkdir(parents=True)
205
        with (aar_libs_folder / "copy-sdl-aars-here.txt").open("w") as f:
206
            f.write(f"Copy {aar} to this folder.\n")
207

208
        print(f"WARNING: copy { aar } to { aar_libs_folder }", file=sys.stderr)
209

210
    # Add the package name to build.gradle
211
    gradle_add_package_name(build_path / "app/build.gradle", args.package_name)
212

213
    # Create entry activity, subclassing SDLActivity
214
    activity = args.package_name[args.package_name.rfind(".") + 1:].capitalize() + "Activity"
215
    activity_path = build_path / "app/src/main/java" / args.package_name.replace(".", "/") / f"{activity}.java"
216
    activity_path.parent.mkdir(parents=True)
217
    with activity_path.open("w") as f:
218
        f.write(textwrap.dedent(f"""
219
            package {args.package_name};
220

221
            import org.libsdl.app.SDLActivity;
222

223
            public class {activity} extends SDLActivity
224
            {{
225
            }}
226
        """))
227

228
    # Add the just-generated activity to the Android manifest
229
    replace_in_file(build_path / "app/src/main/AndroidManifest.xml", 'name="SDLActivity"', f'name="{activity}"')
230

231
    # Update project and build
232
    print("To build and install to a device for testing, run the following:")
233
    print(f"cd {build_path}")
234
    print("./gradlew installDebug")
235
    return 0
236

237
if __name__ == "__main__":
238
    raise SystemExit(main())
239

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.