./make: script to create android builds (#315)
This PR introduces the `./make` script. For now, this script knows how to make Android releases. We have code in https://github.com/ooni/probe-cli/pull/311 that builds other artifacts in this repository. Because our current concern is https://github.com/ooni/probe/issues/1444, in this PR, I have chosen to keep the Android code necessary to fix https://github.com/ooni/probe/issues/1444. Additionally, this PR removes the script to publish to Bintray. This change part of the https://github.com/ooni/probe/issues/1440 cleanup effort.
This commit is contained in:
parent
11087db51a
commit
514ad8d0f5
6
MOBILE/android/.gitignore
vendored
6
MOBILE/android/.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
/oonimkall-sources.jar
|
||||
/oonimkall.aar
|
||||
/*.aar
|
||||
/*.asc
|
||||
/*.jar
|
||||
/*.pom
|
||||
|
|
16
MOBILE/android/go
Executable file
16
MOBILE/android/go
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
|
||||
# print the fullpath of the go we're using so that it's part
|
||||
# of the build logs and we can easily increase our confidence
|
||||
# that we are using the right go binary.
|
||||
|
||||
echo -n "$0: checking for go... "
|
||||
go=`command -v go`
|
||||
if [ -z $go ]; then
|
||||
echo "not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "$go"
|
||||
|
||||
set -x
|
||||
$go "$@"
|
24
MOBILE/android/gomobile
Executable file
24
MOBILE/android/gomobile
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
|
||||
# print the fullpath of the go/gomobile we're using so that it's part
|
||||
# of the build logs and we can easily increase our confidence
|
||||
# that we are using the right go/gomobile binary.
|
||||
|
||||
echo -n "$0: checking for go... "
|
||||
go=`command -v go`
|
||||
if [ -z $go ]; then
|
||||
echo "not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "$go"
|
||||
|
||||
echo -n "$0: checking for gomobile... "
|
||||
gomobile=`command -v gomobile`
|
||||
if [ -z $go ]; then
|
||||
echo "not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "$gomobile"
|
||||
|
||||
set -x
|
||||
$gomobile "$@"
|
804
make
Executable file
804
make
Executable file
|
@ -0,0 +1,804 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
""" Build script for ooniprobe. You can get documentation regarding
|
||||
its usage by running `./make --help`. """
|
||||
|
||||
from __future__ import annotations
|
||||
import datetime
|
||||
|
||||
import getopt
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import NoReturn
|
||||
from typing import Optional
|
||||
from typing import Protocol
|
||||
|
||||
|
||||
def android_cmdlinetools_os() -> str:
|
||||
"""android_cmdlinetools_os maps the name of the current OS to the
|
||||
name used by the android command-line tools file."""
|
||||
system = platform.system()
|
||||
if system == "Linux":
|
||||
return "linux"
|
||||
if system == "Darwin":
|
||||
return "mac"
|
||||
raise RuntimeError(system)
|
||||
|
||||
|
||||
def android_cmdlinetools_version() -> str:
|
||||
"""android_cmdlinetools_version returns the version of the Android
|
||||
command-line tools that we'd like to download."""
|
||||
return "6858069"
|
||||
|
||||
|
||||
def android_cmdlinetools_sha256sum() -> str:
|
||||
"""android_cmdlinetools_sha256sum returns the SHA256 sum of the
|
||||
Android command-line tools zip file."""
|
||||
return {
|
||||
"linux": "87f6dcf41d4e642e37ba03cb2e387a542aa0bd73cb689a9e7152aad40a6e7a08",
|
||||
"mac": "58a55d9c5bcacd7c42170d2cf2c9ae2889c6797a6128307aaf69100636f54a13",
|
||||
}[android_cmdlinetools_os()]
|
||||
|
||||
|
||||
def cachedir() -> str:
|
||||
"""cachedir returns the directory where we cache the SDKs."""
|
||||
return os.path.join(os.path.expandvars("${HOME}"), ".ooniprobe-build")
|
||||
|
||||
|
||||
def goversion() -> str:
|
||||
"""goversion is the Go version we use."""
|
||||
return "1.16.3"
|
||||
|
||||
|
||||
def gopath() -> str:
|
||||
"""gopath is the GOPATH we use."""
|
||||
return os.path.expandvars("${HOME}/go")
|
||||
|
||||
|
||||
def gosha256sum() -> str:
|
||||
"""gosha256sum returns the SHA256 sum of the Go tarball."""
|
||||
return {
|
||||
"linux": {
|
||||
"amd64": "951a3c7c6ce4e56ad883f97d9db74d3d6d80d5fec77455c6ada6c1f7ac4776d2",
|
||||
"arm64": "566b1d6f17d2bc4ad5f81486f0df44f3088c3ed47a3bec4099d8ed9939e90d5d",
|
||||
},
|
||||
"darwin": {
|
||||
"amd64": "6bb1cf421f8abc2a9a4e39140b7397cdae6aca3e8d36dcff39a1a77f4f1170ac",
|
||||
"arm64": "f4e96bbcd5d2d1942f5b55d9e4ab19564da4fad192012f6d7b0b9b055ba4208f",
|
||||
},
|
||||
}[goos()][goarch()]
|
||||
|
||||
|
||||
def goos() -> str:
|
||||
"""goos returns the GOOS value for the current system."""
|
||||
system = platform.system()
|
||||
if system == "Linux":
|
||||
return "linux"
|
||||
if system == "Darwin":
|
||||
return "darwin"
|
||||
raise RuntimeError(system)
|
||||
|
||||
|
||||
def goarch() -> str:
|
||||
"""goarch returns the GOARCH value for the current system."""
|
||||
machine = platform.machine()
|
||||
if machine in ("arm64", "arm", "386", "amd64"):
|
||||
return machine
|
||||
if machine in ("x86", "i386"):
|
||||
return "386"
|
||||
if machine == "x86_64":
|
||||
return "amd64"
|
||||
if machine == "aarch64":
|
||||
return "arm64"
|
||||
raise RuntimeError(machine)
|
||||
|
||||
|
||||
def android_ndk_version() -> str:
|
||||
"""android_ndk_version returns the Android NDK version."""
|
||||
return "22.1.7171670"
|
||||
|
||||
|
||||
def sdkmanager_install_cmd(binpath: str) -> List[str]:
|
||||
"""sdkmanager_install_cmd returns the command line for installing
|
||||
all the required dependencies using the sdkmanager."""
|
||||
return [
|
||||
os.path.join(binpath, "sdkmanager"),
|
||||
"--install",
|
||||
"build-tools;29.0.3",
|
||||
"platforms;android-30",
|
||||
"ndk;{}".format(android_ndk_version()),
|
||||
]
|
||||
|
||||
|
||||
def log(msg: str) -> None:
|
||||
"""log prints a message on the standard error."""
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
class Options(Protocol):
|
||||
"""Options contains the configured options."""
|
||||
|
||||
def debugging(self) -> bool:
|
||||
"""debugging indicates whether to pass -x to `go build...`."""
|
||||
|
||||
def dry_run(self) -> bool:
|
||||
"""dry_run indicates whether to execute commands."""
|
||||
|
||||
def target(self) -> str:
|
||||
"""target is the target to build."""
|
||||
|
||||
def verbose(self) -> bool:
|
||||
"""verbose indicates whether to pass -v to `go build...`."""
|
||||
|
||||
|
||||
class ConfigFromCLI:
|
||||
"""ConfigFromCLI parses options from CLI flags."""
|
||||
|
||||
@classmethod
|
||||
def parse(cls, targets: List[str]) -> ConfigFromCLI:
|
||||
"""parse parses command line options and returns a
|
||||
suitable configuration object."""
|
||||
conf = cls()
|
||||
conf._parse(targets)
|
||||
return conf
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._debugging = False
|
||||
self._dry_run = False
|
||||
self._target = ""
|
||||
self._verbose = False
|
||||
|
||||
def debugging(self) -> bool:
|
||||
return self._debugging
|
||||
|
||||
def dry_run(self) -> bool:
|
||||
return self._dry_run
|
||||
|
||||
def target(self) -> str:
|
||||
return self._target
|
||||
|
||||
def verbose(self) -> bool:
|
||||
return self._verbose
|
||||
|
||||
# The main reason why I am using getopt here is such that I am able
|
||||
# to print a very clear and detailed usage string. (But the same
|
||||
# could be obtained quite likely w/ argparse.)
|
||||
|
||||
__usage_string = """\
|
||||
usage: ./make [-nvx] -t target
|
||||
./make -l
|
||||
./make [--help|-h]
|
||||
|
||||
The first form of the command builds the `target` specified using the
|
||||
`-t` command line flag. If the target has dependencies, this command will
|
||||
build the dependent targets first. The `-n` flag enables a dry run where
|
||||
the command only prints the commands it would run. The `-v` and `-x` flags
|
||||
are passed directly to `go build ...` and `gomobile bind ...`.
|
||||
|
||||
The second form of the command lists all the available targets as
|
||||
a pretty-printed JSON list.
|
||||
|
||||
The third form of the command prints this help screen.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _usage(cls, err: str = "", exitcode: int = 0) -> NoReturn:
|
||||
if err:
|
||||
sys.stderr.write("error: {}\n".format(err))
|
||||
sys.stderr.write(cls.__usage_string)
|
||||
sys.exit(exitcode)
|
||||
|
||||
def _parse(self, targets: List[str]):
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hlnt:vx", ["help"])
|
||||
except getopt.GetoptError as err:
|
||||
self._usage(err=err.msg, exitcode=1)
|
||||
if args:
|
||||
self._usage(err="unexpected number of positional arguments", exitcode=1)
|
||||
for key, value in opts:
|
||||
if key in ("-h", "--help"):
|
||||
self._usage()
|
||||
if key == "-l":
|
||||
sys.stdout.write("{}\n".format(json.dumps(targets, indent=4)))
|
||||
sys.exit(0)
|
||||
if key == "-n":
|
||||
self._dry_run = True
|
||||
continue
|
||||
if key == "-t":
|
||||
self._target = value
|
||||
continue
|
||||
if key == "-v":
|
||||
self._verbose = True
|
||||
continue
|
||||
if key == "-x":
|
||||
self._debugging = True
|
||||
continue
|
||||
raise RuntimeError(key, value)
|
||||
|
||||
if self._target == "": # no arguments is equivalent to --help
|
||||
self._usage()
|
||||
|
||||
if self._target not in targets:
|
||||
sys.stderr.write("unknown target: {}\n".format(self._target))
|
||||
sys.stderr.write("try `./make -l` to see the available targets.\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Engine(Protocol):
|
||||
"""Engine is an engine for building targets."""
|
||||
|
||||
def cat_sed_redirect(
|
||||
self, pattern: str, value: str, source: str, dest: str
|
||||
) -> None:
|
||||
"""cat_sed_redirect does `cat $source|sed "s/$pattern/$value/g" > $dest`."""
|
||||
|
||||
def echo_to_file(self, content: str, filepath: str) -> None:
|
||||
"""echo_to_file writes the content string to the given file."""
|
||||
|
||||
def require(self, *executable: str) -> None:
|
||||
"""require fails if executable is not found in path."""
|
||||
|
||||
def run(
|
||||
self,
|
||||
cmdline: List[str],
|
||||
cwd: Optional[str] = None,
|
||||
extra_env: Optional[Dict[str, str]] = None,
|
||||
inputbytes: Optional[bytes] = None,
|
||||
) -> None:
|
||||
"""run runs the specified command line."""
|
||||
|
||||
|
||||
class CommandRealExecutor:
|
||||
"""CommandRealExecutor executes commands."""
|
||||
|
||||
def cat_sed_redirect(
|
||||
self, pattern: str, value: str, source: str, dest: str
|
||||
) -> None:
|
||||
"""cat_sed_redirect implements Engine.cat_sed_redirect."""
|
||||
with open(source, "r") as sourcefp:
|
||||
data = sourcefp.read().replace(pattern, value)
|
||||
with open(dest, "w") as destfp:
|
||||
destfp.write(data)
|
||||
|
||||
def echo_to_file(self, content: str, filepath: str) -> None:
|
||||
"""echo_to_file implements Engine.echo_to_file"""
|
||||
with open(filepath, "w") as filep:
|
||||
filep.write(content)
|
||||
filep.write("\n")
|
||||
|
||||
def require(self, *executable: str) -> None:
|
||||
"""require implements Engine.require."""
|
||||
# Implemented in CommandDryRunner
|
||||
|
||||
def run(
|
||||
self,
|
||||
cmdline: List[str],
|
||||
cwd: Optional[str] = None,
|
||||
extra_env: Optional[Dict[str, str]] = None,
|
||||
inputbytes: Optional[bytes] = None,
|
||||
) -> None:
|
||||
"""run implements Engine.run."""
|
||||
env = os.environ.copy()
|
||||
if extra_env:
|
||||
for key, value in extra_env.items():
|
||||
env[key] = value
|
||||
subprocess.run(cmdline, check=True, cwd=cwd, env=env, input=inputbytes)
|
||||
|
||||
|
||||
class CommandDryRunner:
|
||||
"""CommandDryRunner is the dry runner."""
|
||||
|
||||
# Implementation note: here we try to log valid bash snippets
|
||||
# such that is really obvious what we are doing.
|
||||
|
||||
def __init__(self):
|
||||
self.__cmdcache: Dict[str, str] = {}
|
||||
|
||||
def cat_sed_redirect(
|
||||
self, pattern: str, value: str, source: str, dest: str
|
||||
) -> None:
|
||||
"""cat_sed_redirect implements Engine.cat_sed_redirect."""
|
||||
log('./make: cat {}|sed "s/{}/{}/g" > {}'.format(source, pattern, value, dest))
|
||||
|
||||
def echo_to_file(self, content: str, filepath: str) -> None:
|
||||
"""echo_to_file implements Engine.echo_to_file"""
|
||||
log("./make: echo '{}' > {}".format(content, filepath))
|
||||
|
||||
def require(self, *executable: str) -> None:
|
||||
"""require implements Engine.require."""
|
||||
for exc in executable:
|
||||
if exc in self.__cmdcache:
|
||||
continue # do not print checks more than once
|
||||
fullpath = shutil.which(exc)
|
||||
if not fullpath:
|
||||
log("./make: checking for {}... not found".format(exc))
|
||||
sys.exit(1)
|
||||
log("./make: checking for {}... {}".format(exc, fullpath))
|
||||
self.__cmdcache[exc] = fullpath
|
||||
|
||||
def run(
|
||||
self,
|
||||
cmdline: List[str],
|
||||
cwd: Optional[str] = None,
|
||||
extra_env: Optional[Dict[str, str]] = None,
|
||||
inputbytes: Optional[bytes] = None,
|
||||
) -> None:
|
||||
"""run implements Engine.run."""
|
||||
cdpart = ""
|
||||
if cwd:
|
||||
cdpart = "cd {} && ".format(cwd)
|
||||
envpart = ""
|
||||
if extra_env:
|
||||
for key, value in extra_env.items():
|
||||
envpart += shlex.join(["{}={}".format(key, value)]) + " "
|
||||
log("./make: {}{}{}".format(cdpart, envpart, shlex.join(cmdline)))
|
||||
|
||||
|
||||
class EngineComposer:
|
||||
"""EngineComposer composes two engines."""
|
||||
|
||||
def __init__(self, first: Engine, second: Engine):
|
||||
self._first = first
|
||||
self._second = second
|
||||
|
||||
def cat_sed_redirect(
|
||||
self, pattern: str, value: str, source: str, dest: str
|
||||
) -> None:
|
||||
"""cat_sed_redirect implements Engine.cat_sed_redirect."""
|
||||
self._first.cat_sed_redirect(pattern, value, source, dest)
|
||||
self._second.cat_sed_redirect(pattern, value, source, dest)
|
||||
|
||||
def echo_to_file(self, content: str, filepath: str) -> None:
|
||||
"""echo_to_file implements Engine.echo_to_file"""
|
||||
self._first.echo_to_file(content, filepath)
|
||||
self._second.echo_to_file(content, filepath)
|
||||
|
||||
def require(self, *executable: str) -> None:
|
||||
"""require implements Engine.require."""
|
||||
self._first.require(*executable)
|
||||
self._second.require(*executable)
|
||||
|
||||
def run(
|
||||
self,
|
||||
cmdline: List[str],
|
||||
cwd: Optional[str] = None,
|
||||
extra_env: Optional[Dict[str, str]] = None,
|
||||
inputbytes: Optional[bytes] = None,
|
||||
) -> None:
|
||||
"""run implements Engine.run."""
|
||||
self._first.run(cmdline, cwd=cwd, extra_env=extra_env, inputbytes=inputbytes)
|
||||
self._second.run(cmdline, cwd=cwd, extra_env=extra_env, inputbytes=inputbytes)
|
||||
|
||||
|
||||
def new_engine(options: Options) -> Engine:
|
||||
"""new_engine creates a new engine instance"""
|
||||
if options.dry_run():
|
||||
return CommandDryRunner()
|
||||
return EngineComposer(CommandDryRunner(), CommandRealExecutor())
|
||||
|
||||
|
||||
class Target(Protocol):
|
||||
"""Target is a target to build."""
|
||||
|
||||
def name(self) -> str:
|
||||
"""name returns the target name."""
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
"""build builds the specified target."""
|
||||
|
||||
|
||||
class SDKGolangGo:
|
||||
"""SDKGolangGo creates ${cachedir}/SDK/golang."""
|
||||
|
||||
# We download a golang SDK from upstream to make sure we
|
||||
# are always using a specific version of golang/go.
|
||||
|
||||
__name = os.path.join(cachedir(), "SDK", "golang")
|
||||
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
if os.path.isdir(self.__name) and not options.dry_run():
|
||||
log("./make: {}: already built".format(self.__name))
|
||||
return
|
||||
log("./make: building {}...".format(self.__name))
|
||||
engine.require("mkdir", "curl", "shasum", "rm", "tar", "echo")
|
||||
filename = "go{}.{}-{}.tar.gz".format(goversion(), goos(), goarch())
|
||||
url = "https://golang.org/dl/{}".format(filename)
|
||||
engine.run(["mkdir", "-p", self.__name])
|
||||
filepath = os.path.join(self.__name, filename)
|
||||
engine.run(["curl", "-fsSLo", filepath, url])
|
||||
sha256file = os.path.join(cachedir(), "SDK", "SHA256")
|
||||
engine.echo_to_file("{} {}".format(gosha256sum(), filepath), sha256file)
|
||||
engine.run(["shasum", "--check", sha256file])
|
||||
engine.run(["rm", sha256file])
|
||||
engine.run(["tar", "-xf", filename], cwd=self.__name)
|
||||
engine.run(["rm", filepath])
|
||||
|
||||
def goroot(self):
|
||||
"""goroot returns the goroot."""
|
||||
return os.path.join(self.__name, "go")
|
||||
|
||||
|
||||
class SDKOONIGo:
|
||||
"""SDKOONIGo creates ${cachedir}/SDK/oonigo."""
|
||||
|
||||
# We use a private fork of golang/go on Android as a
|
||||
# workaround for https://github.com/ooni/probe/issues/1444
|
||||
|
||||
__name = os.path.join(cachedir(), "SDK", "oonigo")
|
||||
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def binpath(self) -> str:
|
||||
return os.path.join(self.__name, "bin")
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
if os.path.isdir(self.__name) and not options.dry_run():
|
||||
log("./make: {}: already built".format(self.__name))
|
||||
return
|
||||
golang_go = SDKGolangGo()
|
||||
golang_go.build(engine, options)
|
||||
log("./make: building {}...".format(self.__name))
|
||||
engine.require("git", "bash")
|
||||
engine.run(
|
||||
[
|
||||
"git",
|
||||
"clone",
|
||||
"-b",
|
||||
"ooni",
|
||||
"--single-branch",
|
||||
"--depth",
|
||||
"8",
|
||||
"https://github.com/ooni/go",
|
||||
self.__name,
|
||||
]
|
||||
)
|
||||
engine.run(
|
||||
["./make.bash"],
|
||||
cwd=os.path.join(self.__name, "src"),
|
||||
extra_env={"GOROOT_BOOTSTRAP": golang_go.goroot()},
|
||||
)
|
||||
|
||||
|
||||
class SDKAndroid:
|
||||
"""SDKAndroid creates ${cachedir}/SDK/android."""
|
||||
|
||||
__name = os.path.join(cachedir(), "SDK", "android")
|
||||
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def home(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def ndk_home(self) -> str:
|
||||
return os.path.join(self.home(), "ndk", android_ndk_version())
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
if os.path.isdir(self.__name) and not options.dry_run():
|
||||
log("./make: {}: already built".format(self.__name))
|
||||
return
|
||||
log("./make: building {}...".format(self.__name))
|
||||
engine.require("mkdir", "curl", "echo", "shasum", "rm", "unzip", "mv", "java")
|
||||
filename = "commandlinetools-{}-{}_latest.zip".format(
|
||||
android_cmdlinetools_os(), android_cmdlinetools_version()
|
||||
)
|
||||
url = "https://dl.google.com/android/repository/{}".format(filename)
|
||||
engine.run(["mkdir", "-p", self.__name])
|
||||
filepath = os.path.join(self.__name, filename)
|
||||
engine.run(["curl", "-fsSLo", filepath, url])
|
||||
sha256file = os.path.join(cachedir(), "SDK", "SHA256")
|
||||
engine.echo_to_file(
|
||||
"{} {}".format(android_cmdlinetools_sha256sum(), filepath), sha256file
|
||||
)
|
||||
engine.run(["shasum", "--check", sha256file])
|
||||
engine.run(["rm", sha256file])
|
||||
engine.run(["unzip", filename], cwd=self.__name)
|
||||
engine.run(["rm", filepath])
|
||||
# See https://stackoverflow.com/a/61176718 to understand why
|
||||
# we need to reorganize the directories like this:
|
||||
engine.run(
|
||||
["mv", "cmdline-tools", android_cmdlinetools_version()], cwd=self.__name
|
||||
)
|
||||
engine.run(["mkdir", "cmdline-tools"], cwd=self.__name)
|
||||
engine.run(
|
||||
["mv", android_cmdlinetools_version(), "cmdline-tools"], cwd=self.__name
|
||||
)
|
||||
engine.run(
|
||||
sdkmanager_install_cmd(
|
||||
os.path.join(
|
||||
self.__name,
|
||||
"cmdline-tools",
|
||||
android_cmdlinetools_version(),
|
||||
"bin",
|
||||
),
|
||||
),
|
||||
inputbytes=b"Y\n", # automatically accept license
|
||||
)
|
||||
|
||||
|
||||
class OONIProbePrivate:
|
||||
"""OONIProbePrivate creates ${cachedir}/github.com/ooni/probe-private."""
|
||||
|
||||
# We use this private repository to copy the psiphon configuration
|
||||
# file to embed into the ooniprobe binaries
|
||||
|
||||
__name = os.path.join(cachedir(), "github.com", "ooni", "probe-private")
|
||||
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def copyfiles(self, engine: Engine) -> None:
|
||||
"""copyfiles copies psiphon config to the repository."""
|
||||
engine.run(
|
||||
[
|
||||
"cp",
|
||||
os.path.join(self.__name, "psiphon-config.json.age"),
|
||||
os.path.join("internal", "engine"),
|
||||
]
|
||||
)
|
||||
engine.run(
|
||||
[
|
||||
"cp",
|
||||
os.path.join(self.__name, "psiphon-config.key"),
|
||||
os.path.join("internal", "engine"),
|
||||
]
|
||||
)
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
if os.path.isdir(self.__name) and not options.dry_run():
|
||||
log("./make: {}: already built".format(self.__name))
|
||||
return
|
||||
log("./make: building {}...".format(self.__name))
|
||||
engine.require("git", "cp")
|
||||
engine.run(
|
||||
[
|
||||
"git",
|
||||
"clone",
|
||||
"git@github.com:ooni/probe-private",
|
||||
self.__name,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class OONIMKAllAAR:
|
||||
"""OONIMKAllAAR creates ./MOBILE/android/oonimkall.aar."""
|
||||
|
||||
__name = os.path.join(".", "MOBILE", "android", "oonimkall.aar")
|
||||
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def aarfile(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def srcfile(self) -> str:
|
||||
return os.path.join(".", "MOBILE", "android", "oonimkall-sources.jar")
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
if os.path.isfile(self.__name) and not options.dry_run():
|
||||
log("./make: {}: already built".format(self.__name))
|
||||
return
|
||||
ooprivate = OONIProbePrivate()
|
||||
ooprivate.build(engine, options)
|
||||
oonigo = SDKOONIGo()
|
||||
oonigo.build(engine, options)
|
||||
android = SDKAndroid()
|
||||
android.build(engine, options)
|
||||
log("./make: building {}...".format(self.__name))
|
||||
ooprivate.copyfiles(engine)
|
||||
engine.require("sh", "javac")
|
||||
self._go_get_gomobile(engine, options, oonigo)
|
||||
self._gomobile_init(engine, oonigo, android)
|
||||
self._gomobile_bind(engine, options, oonigo, android)
|
||||
|
||||
# Implementation note: we use proxy scripts for go and gomobile
|
||||
# that explicitly print what they resolve go and gomobile to using
|
||||
# `command -v`. This gives us extra confidence that we are really
|
||||
# using the oonigo fork of golang/go.
|
||||
|
||||
def _go_get_gomobile(
|
||||
self, engine: Engine, options: Options, oonigo: SDKOONIGo
|
||||
) -> None:
|
||||
# TODO(bassosimone): find a way to run this command without
|
||||
# adding extra dependencies to go.mod and go.sum.
|
||||
cmdline: List[str] = []
|
||||
cmdline.append(os.path.join(".", "MOBILE", "android", "go"))
|
||||
cmdline.append("get")
|
||||
cmdline.append("-u")
|
||||
if options.verbose():
|
||||
cmdline.append("-v")
|
||||
if options.debugging():
|
||||
cmdline.append("-x")
|
||||
cmdline.append("golang.org/x/mobile/cmd/gomobile@latest")
|
||||
engine.run(
|
||||
cmdline,
|
||||
extra_env={
|
||||
"PATH": os.pathsep.join(
|
||||
[
|
||||
oonigo.binpath(), # so we use our go fork
|
||||
os.environ["PATH"], # original path
|
||||
]
|
||||
),
|
||||
"GOPATH": gopath(), # where to install gomobile
|
||||
},
|
||||
)
|
||||
|
||||
def _gomobile_init(
|
||||
self,
|
||||
engine: Engine,
|
||||
oonigo: SDKOONIGo,
|
||||
android: SDKAndroid,
|
||||
) -> None:
|
||||
cmdline: List[str] = []
|
||||
cmdline.append(os.path.join(".", "MOBILE", "android", "gomobile"))
|
||||
cmdline.append("init")
|
||||
engine.run(
|
||||
cmdline,
|
||||
extra_env={
|
||||
"PATH": os.pathsep.join(
|
||||
[
|
||||
os.path.join(gopath(), "bin"), # for gomobile
|
||||
oonigo.binpath(), # for our go fork
|
||||
os.environ["PATH"], # original environment
|
||||
]
|
||||
),
|
||||
"ANDROID_HOME": android.home(),
|
||||
"ANDROID_NDK_HOME": android.ndk_home(),
|
||||
},
|
||||
)
|
||||
|
||||
def _gomobile_bind(
|
||||
self,
|
||||
engine: Engine,
|
||||
options: Options,
|
||||
oonigo: SDKOONIGo,
|
||||
android: SDKAndroid,
|
||||
) -> None:
|
||||
cmdline: List[str] = []
|
||||
cmdline.append(os.path.join(".", "MOBILE", "android", "gomobile"))
|
||||
cmdline.append("bind")
|
||||
if options.verbose():
|
||||
cmdline.append("-v")
|
||||
if options.debugging():
|
||||
cmdline.append("-x")
|
||||
cmdline.append("-target")
|
||||
cmdline.append("android")
|
||||
cmdline.append("-o")
|
||||
cmdline.append(self.__name)
|
||||
cmdline.append("-tags")
|
||||
cmdline.append("ooni_psiphon_config")
|
||||
cmdline.append("-ldflags")
|
||||
cmdline.append("-s -w")
|
||||
cmdline.append("./pkg/oonimkall")
|
||||
engine.run(
|
||||
cmdline,
|
||||
extra_env={
|
||||
"PATH": os.pathsep.join(
|
||||
[
|
||||
os.path.join(gopath(), "bin"), # for gomobile
|
||||
oonigo.binpath(), # for our go fork
|
||||
os.environ["PATH"], # original environment
|
||||
]
|
||||
),
|
||||
"ANDROID_HOME": android.home(),
|
||||
"ANDROID_NDK_HOME": android.ndk_home(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class BundleJAR:
|
||||
"""BundleJAR creates ./MOBILE/android/bundle.jar."""
|
||||
|
||||
# We upload the bundle.jar file to maven central to bless
|
||||
# a new release of the OONI libraries for Android.
|
||||
|
||||
__name = os.path.join(".", "MOBILE", "android", "bundle.jar")
|
||||
|
||||
def name(self) -> str:
|
||||
return self.__name
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
if os.path.isfile(self.__name) and not options.dry_run():
|
||||
log("./make: {}: already built".format(self.__name))
|
||||
return
|
||||
oonimkall = OONIMKAllAAR()
|
||||
oonimkall.build(engine, options)
|
||||
log("./make: building {}...".format(self.__name))
|
||||
engine.require("cp", "gpg", "jar")
|
||||
version = datetime.datetime.now().strftime("%Y.%m.%d-%H%M%S")
|
||||
engine.run(
|
||||
[
|
||||
"cp",
|
||||
oonimkall.aarfile(),
|
||||
os.path.join("MOBILE", "android", "oonimkall-{}.aar".format(version)),
|
||||
]
|
||||
)
|
||||
engine.run(
|
||||
[
|
||||
"cp",
|
||||
oonimkall.srcfile(),
|
||||
os.path.join(
|
||||
"MOBILE", "android", "oonimkall-{}-sources.jar".format(version)
|
||||
),
|
||||
]
|
||||
)
|
||||
engine.cat_sed_redirect(
|
||||
"@VERSION@",
|
||||
version,
|
||||
os.path.join("MOBILE", "template.pom"),
|
||||
os.path.join("MOBILE", "android", "oonimkall-{}.pom".format(version)),
|
||||
)
|
||||
names = (
|
||||
"oonimkall-{}.aar".format(version),
|
||||
"oonimkall-{}-sources.jar".format(version),
|
||||
"oonimkall-{}.pom".format(version),
|
||||
)
|
||||
for name in names:
|
||||
engine.run(
|
||||
[
|
||||
"gpg",
|
||||
"-abu",
|
||||
"simone@openobservatory.org",
|
||||
name,
|
||||
],
|
||||
cwd=os.path.join(".", "MOBILE", "android"),
|
||||
)
|
||||
allnames = [name + ".asc" for name in names]
|
||||
allnames.extend(names)
|
||||
engine.run(
|
||||
[
|
||||
"jar",
|
||||
"-cf",
|
||||
"bundle.jar",
|
||||
*allnames,
|
||||
],
|
||||
cwd=os.path.join(".", "MOBILE", "android"),
|
||||
)
|
||||
|
||||
|
||||
class Android:
|
||||
"""Android is the toplevel android target."""
|
||||
|
||||
def name(self) -> str:
|
||||
return "android"
|
||||
|
||||
def build(self, engine: Engine, options: Options) -> None:
|
||||
bundlejar = BundleJAR()
|
||||
bundlejar.build(engine, options)
|
||||
|
||||
|
||||
TARGETS: List[Target] = [
|
||||
Android(),
|
||||
BundleJAR(),
|
||||
OONIMKAllAAR(),
|
||||
OONIProbePrivate(),
|
||||
SDKAndroid(),
|
||||
SDKGolangGo(),
|
||||
SDKOONIGo(),
|
||||
]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""main function"""
|
||||
alltargets: Dict[str, Target] = dict((t.name(), t) for t in TARGETS)
|
||||
options = ConfigFromCLI.parse(list(alltargets.keys()))
|
||||
engine = new_engine(options)
|
||||
# note that we check whether the target is known in parse()
|
||||
selected = alltargets[options.target()]
|
||||
selected.build(engine, options)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
pkgname=oonimkall
|
||||
version=$(date -u +%Y.%m.%d-%H%M%S)
|
||||
baseurl=https://api.bintray.com/content/ooni/android/$pkgname/$version/org/ooni/$pkgname/$version
|
||||
aarfile=./MOBILE/android/$pkgname.aar
|
||||
aarfile_version=./MOBILE/android/$pkgname-$version.aar
|
||||
ln $aarfile $aarfile_version
|
||||
sourcesfile=./MOBILE/android/$pkgname-sources.jar
|
||||
sourcesfile_version=./MOBILE/android/$pkgname-$version-sources.jar
|
||||
ln $sourcesfile $sourcesfile_version
|
||||
pomfile=./MOBILE/android/$pkgname-$version.pom
|
||||
pomtemplate=./MOBILE/template.pom
|
||||
user=bassosimone
|
||||
cat $pomtemplate|sed "s/@VERSION@/$version/g" > $pomfile
|
||||
if [ -z $MOBILE_BINTRAY_API_KEY ]; then
|
||||
echo "FATAL: missing MOBILE_BINTRAY_API_KEY variable" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
# We currently publish the mobile-staging branch. To cleanup we can fetch all the versions using
|
||||
# the <curl -s $user:$MOBILE_BINTRAY_API_KEY https://api.bintray.com/packages/ooni/android/oonimkall>
|
||||
# query, which returns a list of versions. From such list, we can delete the versions we
|
||||
# don't need using <DELETE /packages/:subject/:repo/:package/versions/:version>.
|
||||
for filename in $aarfile_version $sourcesfile_version $pomfile; do
|
||||
basefilename=$(basename $filename)
|
||||
curl -sT $filename -u $user:$MOBILE_BINTRAY_API_KEY $baseurl/$basefilename?publish=1 >/dev/null
|
||||
done
|
||||
echo "implementation 'org.ooni:oonimkall:$version'"
|
|
@ -1,23 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
pkgname=oonimkall
|
||||
version=$(date -u +%Y.%m.%d-%H%M%S)
|
||||
baseurl=https://api.bintray.com/content/ooni/ios/$pkgname/$version
|
||||
framework=./MOBILE/ios/$pkgname.framework
|
||||
frameworkzip=./MOBILE/ios/$pkgname.framework.zip
|
||||
podspecfile=./MOBILE/ios/$pkgname.podspec
|
||||
podspectemplate=./MOBILE/template.podspec
|
||||
user=bassosimone
|
||||
(cd ./MOBILE/ios && rm -f $pkgname.framework.zip && zip -yr $pkgname.framework.zip $pkgname.framework)
|
||||
cat $podspectemplate|sed "s/@VERSION@/$version/g" > $podspecfile
|
||||
if [ -z $MOBILE_BINTRAY_API_KEY ]; then
|
||||
echo "FATAL: missing MOBILE_BINTRAY_API_KEY variable" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
# We currently publish the mobile-staging branch. To cleanup we can fetch all the versions using
|
||||
# the <curl -s $user:$MOBILE_BINTRAY_API_KEY https://api.bintray.com/packages/ooni/android/oonimkall>
|
||||
# query, which returns a list of versions. From such list, we can delete the versions we
|
||||
# don't need using <DELETE /packages/:subject/:repo/:package/versions/:version>.
|
||||
curl -sT $frameworkzip -u $user:$MOBILE_BINTRAY_API_KEY $baseurl/$pkgname-$version.framework.zip?publish=1 >/dev/null
|
||||
curl -sT $podspecfile -u $user:$MOBILE_BINTRAY_API_KEY $baseurl/$pkgname-$version.podspec?publish=1 >/dev/null
|
||||
echo "pod 'oonimkall', :podspec => 'https://dl.bintray.com/ooni/ios/$pkgname-$version.podspec'"
|
Loading…
Reference in New Issue
Block a user