File: build-osx-bundle.py

package info (click to toggle)
0ad 0.0.26-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 130,460 kB
  • sloc: cpp: 261,824; ansic: 198,392; javascript: 19,067; python: 14,557; sh: 7,629; perl: 4,072; xml: 849; makefile: 741; java: 533; ruby: 229; php: 190; pascal: 30; sql: 21; tcl: 4
file content (150 lines) | stat: -rwxr-xr-x 5,490 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/usr/bin/python3
"""
Builds the OSX bundle from existing elements.
App bundles are intended to be self-contained and portable.
An SDK is required, usually included with Xcode. The SDK ensures
that only those system libraries are used which are available on
the chosen target and compatible systems.

This is Python because plistlib is extremely strict about what it accepts
and it's used by dmgbuild, and saving the Plist doesn't really work otherwise.
"""
import argparse
import datetime
import glob
import os
import plistlib
import shutil

import dmgbuild

parser = argparse.ArgumentParser()
parser.add_argument('bundle_version', help='Bundle version')
parser.add_argument('--min_osx', help='Minimum supported OSX version',
    default='10.12')
parser.add_argument('--bundle_identifier', help='Bundle identifier',
    default='com.wildfiregames.0ad')
parser.add_argument('--dev', help='Turn on dev mode, which isn\'t fit for release but faster',
    action="store_true")
args = parser.parse_args()

BUNDLE_IDENTIFIER = args.bundle_identifier
BUNDLE_VERSION = args.bundle_version
BUNDLE_MIN_OSX_VERSION = args.min_osx

BUNDLE_DMG_NAME = f"0ad-{BUNDLE_VERSION}-alpha-osx64"
BUNDLE_OUTPUT = "0 A.D..app"
BUNDLE_CONTENTS = BUNDLE_OUTPUT + "/Contents"
BUNDLE_BIN = BUNDLE_CONTENTS + "/MacOS"
BUNDLE_RESOURCES = BUNDLE_CONTENTS + "/Resources"
BUNDLE_FRAMEWORKS = BUNDLE_CONTENTS + "/Frameworks"
BUNDLE_PLUGINS = BUNDLE_CONTENTS + "/PlugIns"
BUNDLE_SHAREDSUPPORT = BUNDLE_CONTENTS + "/SharedSupport"

print("Creating bundle directories")

shutil.rmtree(BUNDLE_OUTPUT, ignore_errors=True)
os.makedirs(BUNDLE_BIN)
os.makedirs(BUNDLE_FRAMEWORKS)
os.makedirs(BUNDLE_PLUGINS)
os.makedirs(BUNDLE_RESOURCES)
os.makedirs(BUNDLE_SHAREDSUPPORT)

print("Copying binaries")

# Only pyrogenesis for now, until we find a way to load
#     multiple binaries from one app bundle
# TODO: Would be nicer if we could set this path in premake
shutil.copy("binaries/system/pyrogenesis", BUNDLE_BIN)

print("Copying libs")
shutil.copy("binaries/system/libAtlasUI.dylib", BUNDLE_FRAMEWORKS)
shutil.copy("binaries/system/libCollada.dylib", BUNDLE_FRAMEWORKS)

if not args.dev:
    print("Copying archived game data from archives/")
    for mod in glob.glob("archives/*/"):
        print(f"Copying {mod}")
        shutil.copytree(mod, BUNDLE_RESOURCES + "/data/mods/" + mod.replace("archives/", ""))
else:
    print("Symlinking mods")
    os.makedirs(BUNDLE_RESOURCES + "/data/mods")
    os.chdir(BUNDLE_RESOURCES + "/data/mods")
    for mod in glob.glob("../../../../../binaries/data/mods/*"):
        os.symlink(mod, mod.replace("../../../../../binaries/data/mods/", ""))
    os.chdir("../../../../../")

print("Copying non-archived game data")
shutil.copytree("binaries/data/config", BUNDLE_RESOURCES + "/data/config")
shutil.copytree("binaries/data/l10n", BUNDLE_RESOURCES + "/data/l10n")
shutil.copytree("binaries/data/tools", BUNDLE_RESOURCES + "/data/tools")
# Remove the dev.cfg file or 0 A.D. will assume it's running a dev copy.
os.unlink(BUNDLE_RESOURCES + "/data/config/dev.cfg")

shutil.copy("build/resources/0ad.icns", BUNDLE_RESOURCES)
shutil.copy("build/resources/InfoPlist.strings", BUNDLE_RESOURCES)

print("Copying readmes")
# TODO: Also want copies in the DMG - decide on layout
for file in glob.glob("*.txt"):
    shutil.copy(file, BUNDLE_RESOURCES)
shutil.copy("libraries/LICENSE.txt", BUNDLE_RESOURCES + "/LIB_LICENSE.txt")

print("Creating Info.plist")

with open(BUNDLE_CONTENTS + "/Info.plist", 'wb') as f:
    plistlib.dump({
        'CFBundleName': '0 A.D.',
        'CFBundleIdentifier': BUNDLE_IDENTIFIER,
        'CFBundleVersion': BUNDLE_VERSION,
        'CFBundlePackageType': 'APPL',
        'CFBundleSignature': 'none',
        'CFBundleExecutable': 'pyrogenesis',
        'CFBundleShortVersionString': BUNDLE_VERSION,
        'CFBundleDevelopmentRegion': 'English',
        'CFBundleInfoDictionaryVersion': '6.0',
        'CFBundleIconFile': '0ad',
        'LSHasLocalizedDisplayName': True,
        'LSMinimumSystemVersion': BUNDLE_MIN_OSX_VERSION,
        'NSHumanReadableCopyright': f'Copyright © {datetime.datetime.now().year} Wildfire Games',
        'UTExportedTypeDeclarations': [{
            'UTTypeIdentifier': BUNDLE_IDENTIFIER,
            'UTTypeTagSpecification': {
                'public.filename-extension': ["pyromod"],
            },
            'UTTypeConformsTo': ['public.zip-archive'],
            'UTTypeDescription': '0 A.D. Zipped Mod',
            'UTTypeIconFile': '0ad'
        }],
        'CFBundleDocumentTypes': [{
            'CFBundleTypeExtensions': ['pyromod'],
            'CFBundleTypeRole': 'Editor',
            'CFBundleTypeIconFile': '0ad',
            'LSHandlerRank': 'Owner'
        },
        {
            'CFBundleTypeExtensions': ['zip'],
            'CFBundleTypeRole': 'Viewer',
            'CFBundleTypeIconFile': '0ad',
            'LSHandlerRank': 'Alternate'
        }],
    }, f)

if args.dev:
    print(f"Dev mode bundle complete, located at {BUNDLE_OUTPUT}")
    exit(0)

print("Creating .dmg")

# Package the app into a dmg
dmgbuild.build_dmg(
    filename=BUNDLE_DMG_NAME + ".dmg",
    volume_name=BUNDLE_DMG_NAME,
    settings_file="source/tools/dist/dmgbuild-settings.py",
    defines={
        "app": BUNDLE_OUTPUT,
        "background": "build/resources/dmgbackground.png",
        "icon": "build/resources/0ad.icns"
    })

print(f"Bundle complete! Located in {BUNDLE_OUTPUT}, compressed as {BUNDLE_DMG_NAME}.dmg.")