mirror of
https://github.com/onionshare/onionshare.git
synced 2024-12-29 09:16:24 -05:00
365 lines
12 KiB
Python
365 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import inspect
|
|
import click
|
|
import subprocess
|
|
import shutil
|
|
import glob
|
|
import itertools
|
|
|
|
root = os.path.dirname(
|
|
os.path.dirname(
|
|
os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
|
)
|
|
)
|
|
desktop_dir = os.path.join(root, "desktop")
|
|
|
|
identity_name_application = "Developer ID Application: Micah Lee (N9B95FDWH4)"
|
|
entitlements_plist_path = f"{desktop_dir}/package/Entitlements.plist"
|
|
|
|
|
|
def get_app_path():
|
|
return os.path.join(desktop_dir, "build", "OnionShare.app")
|
|
|
|
|
|
def run(cmd, cwd=None, error_ok=False):
|
|
print(f"{cmd} # cwd={cwd}")
|
|
subprocess.run(cmd, cwd=cwd, check=True)
|
|
|
|
|
|
def get_size(dir):
|
|
size = 0
|
|
for path, dirs, files in os.walk(dir):
|
|
for f in files:
|
|
fp = os.path.join(path, f)
|
|
size += os.path.getsize(fp)
|
|
return size
|
|
|
|
|
|
def sign(path, entitlements, identity):
|
|
run(
|
|
[
|
|
"codesign",
|
|
"--sign",
|
|
identity,
|
|
"--entitlements",
|
|
str(entitlements),
|
|
"--timestamp",
|
|
"--deep",
|
|
"--force",
|
|
"--options",
|
|
"runtime,library",
|
|
str(path),
|
|
]
|
|
)
|
|
|
|
|
|
@click.group()
|
|
def main():
|
|
"""
|
|
macOS build tasks
|
|
"""
|
|
|
|
|
|
@main.command()
|
|
def cleanup_build():
|
|
"""Delete unused PySide6 stuff to save space"""
|
|
app_path = get_app_path()
|
|
before_size = get_size(app_path)
|
|
|
|
print("> Delete unused Qt Frameworks")
|
|
for framework in [
|
|
"QtMultimediaQuick",
|
|
"QtQuickControls2",
|
|
"QtQuickParticles",
|
|
"QtRemoteObjects",
|
|
"Qt3DInput",
|
|
"QtNetworkAuth",
|
|
"QtDataVisualization",
|
|
"QtWebEngineCore",
|
|
"Qt3DQuickRender",
|
|
"Qt3DQuickExtras",
|
|
"QtDesigner",
|
|
"QtNfc",
|
|
"QtQuick3DAssetImport",
|
|
"QtWebEngineWidgets",
|
|
"QtQuickWidgets",
|
|
"Qt3DQuickInput",
|
|
"Qt3DQuickScene2D",
|
|
"Qt3DRender",
|
|
"QtQuick3DRuntimeRender",
|
|
"QtHelp",
|
|
"QtPrintSupport",
|
|
"QtCharts",
|
|
"QtWebSockets",
|
|
"QtQuick3DUtils",
|
|
"QtQuickTemplates2",
|
|
"QtPositioningQuick",
|
|
"Qt3DCore",
|
|
"QtXml",
|
|
"QtSerialPort",
|
|
"QtQuick",
|
|
"QtScxml",
|
|
"QtQml",
|
|
"Qt3DExtras",
|
|
"QtWebChannel",
|
|
"QtMultimedia",
|
|
"QtQmlWorkerScript",
|
|
"QtVirtualKeyboard",
|
|
"QtOpenGL",
|
|
"Qt3DQuick",
|
|
"QtTest",
|
|
"QtPositioning",
|
|
"QtBluetooth",
|
|
"QtQuick3D",
|
|
"Qt3DLogic",
|
|
"QtQuickShapes",
|
|
"QtQuickTest",
|
|
"QtNetwork",
|
|
"QtSvg",
|
|
"QtDesignerComponents",
|
|
"QtMultimediaWidgets",
|
|
"QtQmlModels",
|
|
"Qt3DQuickAnimation",
|
|
"QtSensors",
|
|
"Qt3DAnimation",
|
|
"QtSql",
|
|
"QtConcurrent",
|
|
"QtChartsQml",
|
|
"QtDataVisualizationQml",
|
|
"QtLabsAnimation",
|
|
"QtLabsFolderListModel",
|
|
"QtLabsQmlModels",
|
|
"QtLabsSettings",
|
|
"QtLabsSharedImage",
|
|
"QtLabsWavefrontMesh",
|
|
"QtOpenGLWidgets",
|
|
"QtQmlCore",
|
|
"QtQmlLocalStorage",
|
|
"QtQmlXmlListModel",
|
|
"QtQuick3DAssetUtils",
|
|
"QtQuick3DEffects",
|
|
"QtQuick3DGlslParser",
|
|
"QtQuick3DHelpers",
|
|
"QtQuick3DIblBaker",
|
|
"QtQuick3DParticleEffects",
|
|
"QtQuick3DParticles",
|
|
"QtQuickControls2Impl",
|
|
"QtQuickDialogs2",
|
|
"QtQuickDialogs2QuickImpl",
|
|
"QtQuickDialogs2Utils",
|
|
"QtQuickLayouts",
|
|
"QtQuickTimeline",
|
|
"QtRemoteObjectsQml",
|
|
"QtScxmlQml",
|
|
"QtSensorsQuick",
|
|
"QtShaderTools",
|
|
"QtStateMachine",
|
|
"QtStateMachineQml",
|
|
"QtSvgWidgets",
|
|
"QtUiTools",
|
|
"QtWebEngineQuick",
|
|
"QtWebEngineQuickDelegatesQml"
|
|
]:
|
|
shutil.rmtree(
|
|
f"{app_path}/Contents/MacOS/lib/PySide6/Qt/lib/{framework}.framework"
|
|
)
|
|
print(
|
|
f"Deleted: {app_path}/Contents/MacOS/lib/PySide6/Qt/lib/{framework}.framework"
|
|
)
|
|
try:
|
|
os.remove(f"{app_path}/Contents/MacOS/lib/PySide6/{framework}.abi3.so")
|
|
print(f"Deleted: {app_path}/Contents/MacOS/lib/PySide6/{framework}.abi3.so")
|
|
except FileNotFoundError:
|
|
pass
|
|
try:
|
|
os.remove(f"{app_path}/Contents/MacOS/lib/PySide6/{framework}.pyi")
|
|
print(f"Deleted: {app_path}/Contents/MacOS/lib/PySide6/{framework}.pyi")
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
print("> Move files around so Apple will notarize")
|
|
# https://github.com/marcelotduarte/cx_Freeze/issues/594
|
|
# https://gist.github.com/TechnicalPirate/259a9c24878fcad948452cb148af2a2c#file-custom_bdist_mac-py-L415
|
|
|
|
# Move lib from MacOS into Resources
|
|
os.rename(
|
|
f"{app_path}/Contents/MacOS/lib",
|
|
f"{app_path}/Contents/Resources/lib",
|
|
)
|
|
run(
|
|
["ln", "-s", "../Resources/lib"],
|
|
cwd=f"{app_path}/Contents/MacOS",
|
|
)
|
|
|
|
# Move frameworks from Resources/lib into Frameworks
|
|
os.makedirs(f"{app_path}/Contents/Frameworks", exist_ok=True)
|
|
for framework_filename in glob.glob(
|
|
f"{app_path}/Contents/Resources/lib/PySide6/Qt/lib/Qt*.framework"
|
|
):
|
|
basename = os.path.basename(framework_filename)
|
|
|
|
os.rename(framework_filename, f"{app_path}/Contents/Frameworks/{basename}")
|
|
run(
|
|
["ln", "-s", f"../../../../../Frameworks/{basename}"],
|
|
cwd=f"{app_path}/Contents/Resources/lib/PySide6/Qt/lib",
|
|
)
|
|
if os.path.exists(f"{app_path}/Contents/Frameworks/{basename}/Resources"):
|
|
if not os.path.exists(f"{app_path}/Contents/Frameworks/{basename}/Versions/A/Resources"):
|
|
os.rename(
|
|
f"{app_path}/Contents/Frameworks/{basename}/Resources",
|
|
f"{app_path}/Contents/Frameworks/{basename}/Versions/A/Resources",
|
|
)
|
|
else:
|
|
shutil.rmtree(f"{app_path}/Contents/Frameworks/{basename}/Resources")
|
|
run(
|
|
["ln", "-s", "Versions/A/Resources"],
|
|
cwd=f"{app_path}/Contents/Frameworks/{basename}",
|
|
)
|
|
|
|
try:
|
|
run(
|
|
["ln", "-s", "A", "Current"],
|
|
cwd=f"{app_path}/Contents/Frameworks/{basename}/Versions",
|
|
)
|
|
except:
|
|
pass
|
|
|
|
# Move Qt plugins
|
|
os.rename(
|
|
f"{app_path}/Contents/Resources/lib/PySide6/Qt/plugins",
|
|
f"{app_path}/Contents/Frameworks/plugins",
|
|
)
|
|
run(
|
|
["ln", "-s", "../../../../Frameworks/plugins"],
|
|
cwd=f"{app_path}/Contents/Resources/lib/PySide6/Qt",
|
|
)
|
|
|
|
print("> Delete more unused PySide6 stuff to save space")
|
|
for filename in [
|
|
f"{app_path}/Contents/Resources/lib/PySide6/Designer.app",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/examples",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/glue",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/include",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/lupdate",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/libpyside6.abi3.6.4.dylib",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/Qt/qml",
|
|
f"{app_path}/Contents/Resources/lib/shiboken6/libshiboken6.abi3.6.4.dylib",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/Assistant.app",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/Linguist.app",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/libpyside6qml.abi3.6.4.dylib",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/lrelease",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/qmlformat",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/qmllint",
|
|
f"{app_path}/Contents/Resources/lib/PySide6/qmlls",
|
|
f"{app_path}/Contents/MacOS/QtBluetooth",
|
|
f"{app_path}/Contents/MacOS/QtConcurrent",
|
|
f"{app_path}/Contents/MacOS/QtDesigner",
|
|
f"{app_path}/Contents/MacOS/QtNetworkAuth",
|
|
f"{app_path}/Contents/MacOS/QtNfc",
|
|
f"{app_path}/Contents/MacOS/QtOpenGL",
|
|
f"{app_path}/Contents/MacOS/QtOpenGLWidgets",
|
|
f"{app_path}/Contents/MacOS/QtPositioning",
|
|
f"{app_path}/Contents/MacOS/QtQuick3D",
|
|
f"{app_path}/Contents/MacOS/QtQuick3DRuntimeRender",
|
|
f"{app_path}/Contents/MacOS/QtQuick3DUtils",
|
|
f"{app_path}/Contents/MacOS/QtShaderTools",
|
|
f"{app_path}/Contents/MacOS/QtStateMachine",
|
|
f"{app_path}/Contents/MacOS/QtSvgWidgets",
|
|
f"{app_path}/Contents/MacOS/QtWebChannel",
|
|
f"{app_path}/Contents/MacOS/QtWebEngineCore",
|
|
f"{app_path}/Contents/MacOS/QtWebEngineQuick",
|
|
f"{app_path}/Contents/MacOS/QtXml",
|
|
]:
|
|
if os.path.isfile(filename) or os.path.islink(filename):
|
|
os.remove(filename)
|
|
print(f"Deleted: {filename}")
|
|
elif os.path.isdir(filename):
|
|
shutil.rmtree(filename)
|
|
print(f"Deleted: {filename}")
|
|
else:
|
|
print(f"Cannot delete, filename not found: {filename}")
|
|
|
|
after_size = get_size(f"{app_path}")
|
|
freed_bytes = before_size - after_size
|
|
freed_mb = int(freed_bytes / 1024 / 1024)
|
|
print(f"> Freed {freed_mb} mb")
|
|
|
|
|
|
@main.command()
|
|
@click.argument("app_path")
|
|
def codesign(app_path):
|
|
"""Sign macOS binaries before packaging"""
|
|
for path in itertools.chain(
|
|
glob.glob(f"{app_path}/Contents/Resources/lib/**/*.so", recursive=True),
|
|
glob.glob(f"{app_path}/Contents/Resources/lib/**/*.dylib", recursive=True),
|
|
[
|
|
f"{app_path}/Contents/Frameworks/QtCore.framework/Versions/A/QtCore",
|
|
f"{app_path}/Contents/Frameworks/QtDBus.framework/Versions/A/QtDBus",
|
|
f"{app_path}/Contents/Frameworks/QtGui.framework/Versions/A/QtGui",
|
|
f"{app_path}/Contents/Frameworks/QtWidgets.framework/Versions/A/QtWidgets",
|
|
f"{app_path}/Contents/Resources/lib/Python",
|
|
f"{app_path}/Contents/Resources/lib/onionshare/resources/tor/meek-client",
|
|
f"{app_path}/Contents/Resources/lib/onionshare/resources/tor/obfs4proxy",
|
|
f"{app_path}/Contents/Resources/lib/onionshare/resources/tor/snowflake-client",
|
|
f"{app_path}/Contents/Resources/lib/onionshare/resources/tor/tor",
|
|
f"{app_path}/Contents/Resources/lib/onionshare/resources/tor/libevent-2.1.7.dylib",
|
|
f"{app_path}/Contents/MacOS/onionshare",
|
|
f"{app_path}/Contents/MacOS/onionshare-cli",
|
|
f"{app_path}",
|
|
],
|
|
):
|
|
sign(path, entitlements_plist_path, identity_name_application)
|
|
|
|
print(f"> Signed app bundle: {app_path}")
|
|
|
|
|
|
@main.command()
|
|
@click.argument("app_path")
|
|
def package(app_path):
|
|
"""Build the DMG package"""
|
|
if not os.path.exists("/usr/local/bin/create-dmg"):
|
|
print("> Error: create-dmg is not installed")
|
|
return
|
|
|
|
print("> Create DMG")
|
|
version_filename = f"{root}/cli/onionshare_cli/resources/version.txt"
|
|
with open(version_filename) as f:
|
|
version = f.read().strip()
|
|
|
|
os.makedirs(f"{desktop_dir}/dist", exist_ok=True)
|
|
dmg_path = f"{desktop_dir}/dist/OnionShare-{version}.dmg"
|
|
run(
|
|
[
|
|
"create-dmg",
|
|
"--volname",
|
|
"OnionShare",
|
|
"--volicon",
|
|
f"{desktop_dir}/onionshare/resources/onionshare.icns",
|
|
"--window-size",
|
|
"400",
|
|
"200",
|
|
"--icon-size",
|
|
"100",
|
|
"--icon",
|
|
"OnionShare.app",
|
|
"100",
|
|
"70",
|
|
"--hide-extension",
|
|
"OnionShare.app",
|
|
"--app-drop-link",
|
|
"300",
|
|
"70",
|
|
dmg_path,
|
|
app_path,
|
|
"--identity",
|
|
identity_name_application,
|
|
]
|
|
)
|
|
|
|
print(f"> Finished building DMG: {dmg_path}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|