2021-04-27 16:36:57 +02:00
|
|
|
#!/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
|
|
|
|
|
2021-05-05 11:03:48 +02:00
|
|
|
from typing import Any
|
2021-04-27 16:36:57 +02:00
|
|
|
from typing import Dict
|
|
|
|
from typing import List
|
|
|
|
from typing import NoReturn
|
|
|
|
from typing import Optional
|
|
|
|
from typing import Protocol
|
2021-04-29 11:55:30 +02:00
|
|
|
from typing import Tuple
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
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:
|
2021-05-06 20:44:56 +02:00
|
|
|
"""log prints a message on the standard output."""
|
|
|
|
print(msg, flush=True)
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
class Options(Protocol):
|
|
|
|
"""Options contains the configured options."""
|
|
|
|
|
|
|
|
def debugging(self) -> bool:
|
|
|
|
"""debugging indicates whether to pass -x to `go build...`."""
|
|
|
|
|
2021-04-29 10:30:39 +02:00
|
|
|
def disable_embedding_psiphon_config(self) -> bool:
|
|
|
|
"""disable_embedding_psiphon_config indicates that the user
|
|
|
|
does not want us to embed an encrypted psiphon config file into
|
|
|
|
the binary."""
|
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
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
|
2021-04-29 10:30:39 +02:00
|
|
|
self._disable_embedding_psiphon_config = False
|
2021-04-27 16:36:57 +02:00
|
|
|
self._dry_run = False
|
|
|
|
self._target = ""
|
|
|
|
self._verbose = False
|
|
|
|
|
|
|
|
def debugging(self) -> bool:
|
|
|
|
return self._debugging
|
|
|
|
|
2021-04-29 10:30:39 +02:00
|
|
|
def disable_embedding_psiphon_config(self) -> bool:
|
|
|
|
return self._disable_embedding_psiphon_config
|
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
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.)
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_usage_string = """\
|
2021-04-29 10:30:39 +02:00
|
|
|
usage: ./make [--disable-embedding-psiphon-config] [-nvx] -t target
|
2021-04-27 16:36:57 +02:00
|
|
|
./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
|
2021-04-29 10:30:39 +02:00
|
|
|
are passed directly to `go build ...` and `gomobile bind ...`. The
|
|
|
|
`--disable-embedding-psiphon-config` flag causes this command to disable
|
|
|
|
embedding a psiphon config file into the generated binary; you should
|
|
|
|
use this option when you cannot clone the private repository containing
|
|
|
|
the psiphon configuration file.
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
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))
|
2021-05-05 12:32:45 +02:00
|
|
|
sys.stderr.write(cls._usage_string)
|
2021-04-27 16:36:57 +02:00
|
|
|
sys.exit(exitcode)
|
|
|
|
|
|
|
|
def _parse(self, targets: List[str]):
|
|
|
|
try:
|
2021-04-29 10:30:39 +02:00
|
|
|
opts, args = getopt.getopt(
|
|
|
|
sys.argv[1:], "hlnt:vx", ["disable-embedding-psiphon-config", "help"]
|
|
|
|
)
|
2021-04-27 16:36:57 +02:00
|
|
|
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:
|
2021-04-29 10:30:39 +02:00
|
|
|
if key == "--disable-embedding-psiphon-config":
|
|
|
|
self._disable_embedding_psiphon_config = True
|
|
|
|
continue
|
2021-04-27 16:36:57 +02:00
|
|
|
if key in ("-h", "--help"):
|
|
|
|
self._usage()
|
|
|
|
if key == "-l":
|
2021-05-05 14:26:19 +02:00
|
|
|
sys.stdout.write("{}\n".format(json.dumps(sorted(targets), indent=4)))
|
2021-04-27 16:36:57 +02:00
|
|
|
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."""
|
|
|
|
|
2021-04-29 11:55:30 +02:00
|
|
|
def backticks(
|
|
|
|
self,
|
|
|
|
output_variable: str,
|
|
|
|
cmdline: List[str],
|
2021-05-05 10:42:39 +02:00
|
|
|
) -> bytes:
|
|
|
|
"""backticks executes output_variable=`*cmdline` and returns
|
|
|
|
the output emitted by the command to the caller."""
|
2021-04-29 11:55:30 +02:00
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
def cat_sed_redirect(
|
2021-04-29 11:55:30 +02:00
|
|
|
self, patterns: List[Tuple[str, str]], source: str, dest: str
|
2021-04-27 16:36:57 +02:00
|
|
|
) -> None:
|
2021-04-29 11:55:30 +02:00
|
|
|
"""cat_sed_redirect does
|
|
|
|
`cat $source|sed -e "s/$patterns[0][0]/$patterns[0][1]/g" ... > $dest`."""
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
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,
|
|
|
|
inputbytes: Optional[bytes] = None,
|
|
|
|
) -> None:
|
|
|
|
"""run runs the specified command line."""
|
|
|
|
|
2021-05-05 11:03:48 +02:00
|
|
|
def setenv(self, key: str, value: str) -> Optional[str]:
|
|
|
|
"""setenv sets an environment variable and returns the
|
|
|
|
previous value of such variable (or None)."""
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
def unsetenv(self, key: str) -> None:
|
|
|
|
"""unsetenv clears an environment variable."""
|
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
|
2021-05-05 10:30:27 +02:00
|
|
|
class CommandExecutor:
|
|
|
|
"""CommandExecutor executes commands."""
|
|
|
|
|
|
|
|
def __init__(self, dry_runner: Engine):
|
|
|
|
self._dry_runner = dry_runner
|
2021-04-27 16:36:57 +02:00
|
|
|
|
2021-04-29 11:55:30 +02:00
|
|
|
def backticks(
|
|
|
|
self,
|
|
|
|
output_variable: str,
|
|
|
|
cmdline: List[str],
|
2021-05-05 10:42:39 +02:00
|
|
|
) -> bytes:
|
2021-04-29 11:55:30 +02:00
|
|
|
"""backticks implements Engine.backticks"""
|
2021-05-05 10:42:39 +02:00
|
|
|
out = self._dry_runner.backticks(output_variable, cmdline)
|
2021-05-05 10:30:27 +02:00
|
|
|
# Nothing else to do, because backticks is fully
|
|
|
|
# implemented by CommandDryRunner.
|
2021-05-05 10:42:39 +02:00
|
|
|
return out
|
2021-04-29 11:55:30 +02:00
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
def cat_sed_redirect(
|
2021-04-29 11:55:30 +02:00
|
|
|
self, patterns: List[Tuple[str, str]], source: str, dest: str
|
2021-04-27 16:36:57 +02:00
|
|
|
) -> None:
|
|
|
|
"""cat_sed_redirect implements Engine.cat_sed_redirect."""
|
2021-05-05 10:30:27 +02:00
|
|
|
self._dry_runner.cat_sed_redirect(patterns, source, dest)
|
2021-04-27 16:36:57 +02:00
|
|
|
with open(source, "r") as sourcefp:
|
2021-04-29 11:55:30 +02:00
|
|
|
data = sourcefp.read()
|
|
|
|
for p, v in patterns:
|
|
|
|
data = data.replace(p, v)
|
2021-04-27 16:36:57 +02:00
|
|
|
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"""
|
2021-05-05 10:30:27 +02:00
|
|
|
self._dry_runner.echo_to_file(content, filepath)
|
2021-04-27 16:36:57 +02:00
|
|
|
with open(filepath, "w") as filep:
|
|
|
|
filep.write(content)
|
|
|
|
filep.write("\n")
|
|
|
|
|
|
|
|
def require(self, *executable: str) -> None:
|
|
|
|
"""require implements Engine.require."""
|
2021-05-05 11:33:51 +02:00
|
|
|
for exc in executable:
|
|
|
|
self._dry_runner.require(exc)
|
|
|
|
fullpath = shutil.which(exc)
|
|
|
|
if not fullpath:
|
|
|
|
log("checking for {}... not found".format(exc))
|
|
|
|
sys.exit(1)
|
|
|
|
log("checking for {}... {}".format(exc, fullpath))
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def run(
|
|
|
|
self,
|
|
|
|
cmdline: List[str],
|
|
|
|
cwd: Optional[str] = None,
|
|
|
|
inputbytes: Optional[bytes] = None,
|
|
|
|
) -> None:
|
|
|
|
"""run implements Engine.run."""
|
2021-05-05 11:03:48 +02:00
|
|
|
self._dry_runner.run(cmdline, cwd, inputbytes)
|
|
|
|
subprocess.run(cmdline, check=True, cwd=cwd, input=inputbytes)
|
|
|
|
|
|
|
|
def setenv(self, key: str, value: str) -> Optional[str]:
|
2021-04-29 19:24:25 +02:00
|
|
|
"""setenv implements Engine.setenv."""
|
2021-05-05 11:33:51 +02:00
|
|
|
# Nothing else to do, because setenv is fully
|
2021-05-05 11:03:48 +02:00
|
|
|
# implemented by CommandDryRunner.
|
|
|
|
return self._dry_runner.setenv(key, value)
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
def unsetenv(self, key: str) -> None:
|
|
|
|
"""unsetenv implements Engine.unsetenv."""
|
2021-05-05 11:33:51 +02:00
|
|
|
# Nothing else to do, because unsetenv is fully
|
2021-05-05 11:03:48 +02:00
|
|
|
# implemented by CommandDryRunner.
|
2021-05-05 10:30:27 +02:00
|
|
|
self._dry_runner.unsetenv(key)
|
2021-04-29 19:24:25 +02:00
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2021-04-29 11:55:30 +02:00
|
|
|
def backticks(
|
|
|
|
self,
|
|
|
|
output_variable: str,
|
|
|
|
cmdline: List[str],
|
2021-05-05 10:42:39 +02:00
|
|
|
) -> bytes:
|
2021-04-29 11:55:30 +02:00
|
|
|
"""backticks implements Engine.backticks"""
|
2021-04-29 19:24:25 +02:00
|
|
|
log("./make: {}=`{}`".format(output_variable, shlex.join(cmdline)))
|
2021-04-29 11:55:30 +02:00
|
|
|
# implemented here because we want to see the result of backticks
|
|
|
|
# command invocations when we're doing a dry run
|
|
|
|
popen = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
|
|
|
|
stdout = popen.communicate()[0]
|
|
|
|
if popen.returncode != 0:
|
|
|
|
raise RuntimeError(popen.returncode)
|
2021-05-05 10:42:39 +02:00
|
|
|
return stdout
|
2021-04-29 11:55:30 +02:00
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
def cat_sed_redirect(
|
2021-04-29 11:55:30 +02:00
|
|
|
self, patterns: List[Tuple[str, str]], source: str, dest: str
|
2021-04-27 16:36:57 +02:00
|
|
|
) -> None:
|
|
|
|
"""cat_sed_redirect implements Engine.cat_sed_redirect."""
|
2021-04-29 11:55:30 +02:00
|
|
|
out = "./make: cat {}|sed".format(source)
|
|
|
|
for p, v in patterns:
|
|
|
|
out += " -e 's/{}/{}/g'".format(p, v)
|
|
|
|
out += " > {}".format(dest)
|
|
|
|
log(out)
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
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:
|
2021-05-05 11:33:51 +02:00
|
|
|
log(f"./make: echo -n 'checking for {exc}... '")
|
|
|
|
log("./make: command -v %s || { echo 'not found'; exit 1 }" % exc)
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def run(
|
|
|
|
self,
|
|
|
|
cmdline: List[str],
|
|
|
|
cwd: Optional[str] = None,
|
|
|
|
inputbytes: Optional[bytes] = None,
|
|
|
|
) -> None:
|
|
|
|
"""run implements Engine.run."""
|
|
|
|
cdpart = ""
|
|
|
|
if cwd:
|
|
|
|
cdpart = "cd {} && ".format(cwd)
|
2021-05-05 11:03:48 +02:00
|
|
|
log("./make: {}{}".format(cdpart, shlex.join(cmdline)))
|
2021-04-27 16:36:57 +02:00
|
|
|
|
2021-05-05 11:03:48 +02:00
|
|
|
def setenv(self, key: str, value: str) -> Optional[str]:
|
2021-04-29 19:24:25 +02:00
|
|
|
"""setenv implements Engine.setenv."""
|
|
|
|
log("./make: export {}={}".format(key, shlex.join([value])))
|
2021-05-05 11:03:48 +02:00
|
|
|
prev = os.environ.get(key)
|
|
|
|
os.environ[key] = value
|
|
|
|
return prev
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
def unsetenv(self, key: str) -> None:
|
|
|
|
"""unsetenv implements Engine.unsetenv."""
|
|
|
|
log("./make: unset {}".format(key))
|
2021-05-05 11:03:48 +02:00
|
|
|
del os.environ[key]
|
2021-04-29 19:24:25 +02:00
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def new_engine(options: Options) -> Engine:
|
|
|
|
"""new_engine creates a new engine instance"""
|
2021-05-05 10:30:27 +02:00
|
|
|
out: Engine = CommandDryRunner()
|
|
|
|
if not options.dry_run():
|
|
|
|
out = CommandExecutor(out)
|
|
|
|
return out
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
|
2021-05-05 11:03:48 +02:00
|
|
|
class Environ:
|
|
|
|
"""Environ creates a context where specific environment
|
|
|
|
variables are set. They will be restored to their previous
|
|
|
|
value when we are leaving the context."""
|
|
|
|
|
|
|
|
def __init__(self, engine: Engine, key: str, value: str):
|
|
|
|
self._engine = engine
|
|
|
|
self._key = key
|
|
|
|
self._value = value
|
|
|
|
self._prev: Optional[str] = None
|
|
|
|
|
|
|
|
def __enter__(self) -> None:
|
|
|
|
self._prev = self._engine.setenv(self._key, self._value)
|
|
|
|
|
|
|
|
def __exit__(self, type: Any, value: Any, traceback: Any) -> bool:
|
|
|
|
if self._prev is None:
|
|
|
|
self._engine.unsetenv(self._key)
|
2021-05-05 14:26:19 +02:00
|
|
|
return False # progagate exc
|
2021-05-05 11:03:48 +02:00
|
|
|
self._engine.setenv(self._key, self._prev)
|
2021-05-05 14:26:19 +02:00
|
|
|
return False # progagate exc
|
2021-05-05 11:03:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
class AugmentedPath(Environ):
|
|
|
|
"""AugementedPath is an Environ that prepends the required
|
|
|
|
directory to the currently existing search path."""
|
|
|
|
|
|
|
|
def __init__(self, engine: Engine, directory: str):
|
|
|
|
value = os.pathsep.join([directory, os.environ["PATH"]])
|
|
|
|
super().__init__(engine, "PATH", value)
|
|
|
|
|
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
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.
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(cachedir(), "SDK", "golang")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
2021-04-29 10:30:39 +02:00
|
|
|
def binpath(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return os.path.join(self._name, "go", "bin")
|
2021-04-29 10:30:39 +02:00
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isdir(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
return
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.require("mkdir", "curl", "shasum", "rm", "tar", "echo")
|
|
|
|
filename = "go{}.{}-{}.tar.gz".format(goversion(), goos(), goarch())
|
|
|
|
url = "https://golang.org/dl/{}".format(filename)
|
2021-05-05 12:32:45 +02:00
|
|
|
engine.run(["mkdir", "-p", self._name])
|
|
|
|
filepath = os.path.join(self._name, filename)
|
2021-04-27 16:36:57 +02:00
|
|
|
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])
|
2021-05-05 12:32:45 +02:00
|
|
|
engine.run(["tar", "-xf", filename], cwd=self._name)
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.run(["rm", filepath])
|
|
|
|
|
|
|
|
def goroot(self):
|
|
|
|
"""goroot returns the goroot."""
|
2021-05-05 12:32:45 +02:00
|
|
|
return os.path.join(self._name, "go")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(cachedir(), "SDK", "oonigo")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def binpath(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return os.path.join(self._name, "bin")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isdir(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
return
|
|
|
|
golang_go = SDKGolangGo()
|
|
|
|
golang_go.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.require("git", "bash")
|
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"git",
|
|
|
|
"clone",
|
|
|
|
"-b",
|
|
|
|
"ooni",
|
|
|
|
"--single-branch",
|
|
|
|
"--depth",
|
|
|
|
"8",
|
|
|
|
"https://github.com/ooni/go",
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name,
|
2021-04-27 16:36:57 +02:00
|
|
|
]
|
|
|
|
)
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "GOROOT_BOOTSTRAP", golang_go.goroot()):
|
|
|
|
engine.run(
|
|
|
|
["./make.bash"],
|
2021-05-05 12:32:45 +02:00
|
|
|
cwd=os.path.join(self._name, "src"),
|
2021-05-05 11:03:48 +02:00
|
|
|
)
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
class SDKAndroid:
|
|
|
|
"""SDKAndroid creates ${cachedir}/SDK/android."""
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(cachedir(), "SDK", "android")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def home(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def ndk_home(self) -> str:
|
|
|
|
return os.path.join(self.home(), "ndk", android_ndk_version())
|
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isdir(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
return
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
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)
|
2021-05-05 12:32:45 +02:00
|
|
|
engine.run(["mkdir", "-p", self._name])
|
|
|
|
filepath = os.path.join(self._name, filename)
|
2021-04-27 16:36:57 +02:00
|
|
|
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])
|
2021-05-05 12:32:45 +02:00
|
|
|
engine.run(["unzip", filename], cwd=self._name)
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.run(["rm", filepath])
|
|
|
|
# See https://stackoverflow.com/a/61176718 to understand why
|
|
|
|
# we need to reorganize the directories like this:
|
|
|
|
engine.run(
|
2021-05-05 12:32:45 +02:00
|
|
|
["mv", "cmdline-tools", android_cmdlinetools_version()], cwd=self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
)
|
2021-05-05 12:32:45 +02:00
|
|
|
engine.run(["mkdir", "cmdline-tools"], cwd=self._name)
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.run(
|
2021-05-05 12:32:45 +02:00
|
|
|
["mv", android_cmdlinetools_version(), "cmdline-tools"], cwd=self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
)
|
|
|
|
engine.run(
|
|
|
|
sdkmanager_install_cmd(
|
|
|
|
os.path.join(
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name,
|
2021-04-27 16:36:57 +02:00
|
|
|
"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
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(cachedir(), "github.com", "ooni", "probe-private")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
2021-04-29 10:30:39 +02:00
|
|
|
def copyfiles(self, engine: Engine, options: Options) -> None:
|
2021-04-27 16:36:57 +02:00
|
|
|
"""copyfiles copies psiphon config to the repository."""
|
2021-04-29 10:30:39 +02:00
|
|
|
if options.disable_embedding_psiphon_config():
|
|
|
|
log("./make: copy psiphon config: disabled by command line flags")
|
|
|
|
return
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"cp",
|
2021-05-05 12:32:45 +02:00
|
|
|
os.path.join(self._name, "psiphon-config.json.age"),
|
2021-04-27 16:36:57 +02:00
|
|
|
os.path.join("internal", "engine"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"cp",
|
2021-05-05 12:32:45 +02:00
|
|
|
os.path.join(self._name, "psiphon-config.key"),
|
2021-04-27 16:36:57 +02:00
|
|
|
os.path.join("internal", "engine"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isdir(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
return
|
2021-04-29 10:30:39 +02:00
|
|
|
if options.disable_embedding_psiphon_config():
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: {}: disabled by command line flags".format(self._name))
|
2021-04-29 10:30:39 +02:00
|
|
|
return
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.require("git", "cp")
|
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"git",
|
|
|
|
"clone",
|
|
|
|
"git@github.com:ooni/probe-private",
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name,
|
2021-04-27 16:36:57 +02:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class OONIMKAllAAR:
|
|
|
|
"""OONIMKAllAAR creates ./MOBILE/android/oonimkall.aar."""
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(".", "MOBILE", "android", "oonimkall.aar")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def aarfile(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def srcfile(self) -> str:
|
|
|
|
return os.path.join(".", "MOBILE", "android", "oonimkall-sources.jar")
|
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
return
|
|
|
|
ooprivate = OONIProbePrivate()
|
|
|
|
ooprivate.build(engine, options)
|
|
|
|
oonigo = SDKOONIGo()
|
|
|
|
oonigo.build(engine, options)
|
|
|
|
android = SDKAndroid()
|
|
|
|
android.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-29 10:30:39 +02:00
|
|
|
ooprivate.copyfiles(engine, options)
|
2021-04-27 16:36:57 +02:00
|
|
|
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] = []
|
2021-05-05 11:33:51 +02:00
|
|
|
cmdline.append("go")
|
2021-04-27 16:36:57 +02:00
|
|
|
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")
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "GOPATH", gopath()):
|
|
|
|
with AugmentedPath(engine, oonigo.binpath()):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def _gomobile_init(
|
|
|
|
self,
|
|
|
|
engine: Engine,
|
|
|
|
oonigo: SDKOONIGo,
|
|
|
|
android: SDKAndroid,
|
|
|
|
) -> None:
|
|
|
|
cmdline: List[str] = []
|
2021-05-05 11:33:51 +02:00
|
|
|
cmdline.append("gomobile")
|
2021-04-27 16:36:57 +02:00
|
|
|
cmdline.append("init")
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "ANDROID_HOME", android.home()):
|
|
|
|
with Environ(engine, "ANDROID_NDK_HOME", android.ndk_home()):
|
|
|
|
with AugmentedPath(engine, oonigo.binpath()):
|
|
|
|
with AugmentedPath(engine, os.path.join(gopath(), "bin")):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("gomobile", "go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def _gomobile_bind(
|
|
|
|
self,
|
|
|
|
engine: Engine,
|
|
|
|
options: Options,
|
|
|
|
oonigo: SDKOONIGo,
|
|
|
|
android: SDKAndroid,
|
|
|
|
) -> None:
|
|
|
|
cmdline: List[str] = []
|
2021-05-05 11:33:51 +02:00
|
|
|
cmdline.append("gomobile")
|
2021-04-27 16:36:57 +02:00
|
|
|
cmdline.append("bind")
|
|
|
|
if options.verbose():
|
|
|
|
cmdline.append("-v")
|
|
|
|
if options.debugging():
|
|
|
|
cmdline.append("-x")
|
|
|
|
cmdline.append("-target")
|
|
|
|
cmdline.append("android")
|
|
|
|
cmdline.append("-o")
|
2021-05-05 12:32:45 +02:00
|
|
|
cmdline.append(self._name)
|
2021-04-29 10:30:39 +02:00
|
|
|
if not options.disable_embedding_psiphon_config():
|
|
|
|
cmdline.append("-tags")
|
|
|
|
cmdline.append("ooni_psiphon_config")
|
2021-04-27 16:36:57 +02:00
|
|
|
cmdline.append("-ldflags")
|
|
|
|
cmdline.append("-s -w")
|
|
|
|
cmdline.append("./pkg/oonimkall")
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "ANDROID_HOME", android.home()):
|
|
|
|
with Environ(engine, "ANDROID_NDK_HOME", android.ndk_home()):
|
|
|
|
with AugmentedPath(engine, oonigo.binpath()):
|
|
|
|
with AugmentedPath(engine, os.path.join(gopath(), "bin")):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("gomobile", "go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
|
2021-05-05 14:26:19 +02:00
|
|
|
def sign(engine: Engine, filepath: str) -> str:
|
|
|
|
"""sign signs the given filepath using pgp and returns
|
|
|
|
the filepath of the signature file."""
|
|
|
|
engine.require("gpg")
|
|
|
|
user = "simone@openobservatory.org"
|
|
|
|
engine.run(["gpg", "-abu", user, filepath])
|
|
|
|
return filepath + ".asc"
|
|
|
|
|
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
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.
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(".", "MOBILE", "android", "bundle.jar")
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
return
|
|
|
|
oonimkall = OONIMKAllAAR()
|
|
|
|
oonimkall.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-27 16:36:57 +02:00
|
|
|
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(
|
2021-04-29 19:24:25 +02:00
|
|
|
[
|
|
|
|
("@VERSION@", version),
|
|
|
|
],
|
2021-04-27 16:36:57 +02:00
|
|
|
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),
|
|
|
|
)
|
2021-05-05 14:26:19 +02:00
|
|
|
allnames: List[str] = []
|
2021-04-27 16:36:57 +02:00
|
|
|
for name in names:
|
2021-05-05 14:26:19 +02:00
|
|
|
allnames.append(name)
|
|
|
|
allnames.append(sign(engine, name))
|
2021-04-27 16:36:57 +02:00
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"jar",
|
|
|
|
"-cf",
|
|
|
|
"bundle.jar",
|
|
|
|
*allnames,
|
|
|
|
],
|
|
|
|
cwd=os.path.join(".", "MOBILE", "android"),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-05-05 14:26:19 +02:00
|
|
|
class Phony:
|
|
|
|
"""Phony is a phony target that executes one or more other targets."""
|
|
|
|
|
|
|
|
def __init__(self, name: str, depends: List[Target]):
|
|
|
|
self._name = name
|
|
|
|
self._depends = depends
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 14:26:19 +02:00
|
|
|
return self._name
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 14:26:19 +02:00
|
|
|
for dep in self._depends:
|
|
|
|
dep.build(engine, options)
|
|
|
|
|
|
|
|
|
|
|
|
# Android is the top-level "android" target
|
|
|
|
ANDROID = Phony("android", [BundleJAR()])
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
|
2021-04-29 10:30:39 +02:00
|
|
|
class OONIMKAllFramework:
|
|
|
|
"""OONIMKAllFramework creates ./MOBILE/ios/oonimkall.framework."""
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(".", "MOBILE", "ios", "oonimkall.framework")
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-29 10:30:39 +02:00
|
|
|
return
|
|
|
|
ooprivate = OONIProbePrivate()
|
|
|
|
ooprivate.build(engine, options)
|
|
|
|
gogo = SDKGolangGo()
|
|
|
|
gogo.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-29 10:30:39 +02:00
|
|
|
ooprivate.copyfiles(engine, options)
|
|
|
|
self._go_get_gomobile(engine, options, gogo)
|
|
|
|
self._gomobile_init(engine, gogo)
|
|
|
|
self._gomobile_bind(engine, options, gogo)
|
|
|
|
|
|
|
|
def _go_get_gomobile(
|
|
|
|
self,
|
|
|
|
engine: Engine,
|
|
|
|
options: Options,
|
|
|
|
gogo: SDKGolangGo,
|
|
|
|
) -> None:
|
|
|
|
# TODO(bassosimone): find a way to run this command without
|
|
|
|
# adding extra dependencies to go.mod and go.sum.
|
|
|
|
cmdline: List[str] = []
|
2021-05-05 11:33:51 +02:00
|
|
|
cmdline.append("go")
|
2021-04-29 10:30:39 +02:00
|
|
|
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")
|
2021-05-05 11:03:48 +02:00
|
|
|
with AugmentedPath(engine, gogo.binpath()):
|
|
|
|
with Environ(engine, "GOPATH", gopath()):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def _gomobile_init(
|
|
|
|
self,
|
|
|
|
engine: Engine,
|
|
|
|
gogo: SDKGolangGo,
|
|
|
|
) -> None:
|
|
|
|
cmdline: List[str] = []
|
2021-05-05 11:33:51 +02:00
|
|
|
cmdline.append("gomobile")
|
2021-04-29 10:30:39 +02:00
|
|
|
cmdline.append("init")
|
2021-05-05 11:03:48 +02:00
|
|
|
with AugmentedPath(engine, os.path.join(gopath(), "bin")):
|
|
|
|
with AugmentedPath(engine, gogo.binpath()):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("gomobile", "go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def _gomobile_bind(
|
|
|
|
self,
|
|
|
|
engine: Engine,
|
|
|
|
options: Options,
|
|
|
|
gogo: SDKGolangGo,
|
|
|
|
) -> None:
|
|
|
|
cmdline: List[str] = []
|
2021-05-05 11:33:51 +02:00
|
|
|
cmdline.append("gomobile")
|
2021-04-29 10:30:39 +02:00
|
|
|
cmdline.append("bind")
|
|
|
|
if options.verbose():
|
|
|
|
cmdline.append("-v")
|
|
|
|
if options.debugging():
|
|
|
|
cmdline.append("-x")
|
|
|
|
cmdline.append("-target")
|
|
|
|
cmdline.append("ios")
|
|
|
|
cmdline.append("-o")
|
2021-05-05 12:32:45 +02:00
|
|
|
cmdline.append(self._name)
|
2021-04-29 10:30:39 +02:00
|
|
|
if not options.disable_embedding_psiphon_config():
|
|
|
|
cmdline.append("-tags")
|
|
|
|
cmdline.append("ooni_psiphon_config")
|
|
|
|
cmdline.append("-ldflags")
|
|
|
|
cmdline.append("-s -w")
|
|
|
|
cmdline.append("./pkg/oonimkall")
|
2021-05-05 11:03:48 +02:00
|
|
|
with AugmentedPath(engine, os.path.join(gopath(), "bin")):
|
|
|
|
with AugmentedPath(engine, gogo.binpath()):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("gomobile", "go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
class OONIMKAllFrameworkZip:
|
|
|
|
"""OONIMKAllFrameworkZip creates ./MOBILE/ios/oonimkall.framework.zip."""
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(".", "MOBILE", "ios", "oonimkall.framework.zip")
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-29 10:30:39 +02:00
|
|
|
return
|
|
|
|
engine.require("zip", "rm")
|
|
|
|
ooframework = OONIMKAllFramework()
|
|
|
|
ooframework.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-29 10:30:39 +02:00
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"rm",
|
|
|
|
"-rf",
|
|
|
|
"oonimkall.framework.zip",
|
|
|
|
],
|
|
|
|
cwd=os.path.join(".", "MOBILE", "ios"),
|
|
|
|
)
|
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"zip",
|
|
|
|
"-yr",
|
|
|
|
"oonimkall.framework.zip",
|
|
|
|
"oonimkall.framework",
|
|
|
|
],
|
|
|
|
cwd=os.path.join(".", "MOBILE", "ios"),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class OONIMKAllPodspec:
|
|
|
|
"""OONIMKAllPodspec creates ./MOBILE/ios/oonimkall.podspec."""
|
|
|
|
|
2021-05-05 12:32:45 +02:00
|
|
|
_name = os.path.join(".", "MOBILE", "ios", "oonimkall.podspec")
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("./make: {}: already built".format(self._name))
|
2021-04-29 10:30:39 +02:00
|
|
|
return
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("cat", "sed")
|
2021-05-05 10:42:39 +02:00
|
|
|
output = engine.backticks("RELEASE", ["git", "describe", "--tags"])
|
|
|
|
release = output.decode("utf-8").strip()
|
2021-04-29 10:30:39 +02:00
|
|
|
version = datetime.datetime.now().strftime("%Y.%m.%d-%H%M%S")
|
|
|
|
engine.cat_sed_redirect(
|
2021-04-29 11:55:30 +02:00
|
|
|
[("@VERSION@", version), ("@RELEASE@", release)],
|
2021-04-29 10:30:39 +02:00
|
|
|
os.path.join(".", "MOBILE", "template.podspec"),
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name,
|
2021-04-29 10:30:39 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-05-05 14:26:19 +02:00
|
|
|
# IOS is the top-level "ios" target.
|
|
|
|
IOS = Phony("ios", [OONIMKAllFrameworkZip(), OONIMKAllPodspec()])
|
2021-04-29 10:30:39 +02:00
|
|
|
|
|
|
|
|
2021-04-29 19:24:25 +02:00
|
|
|
class MiniOONIDarwinOrWindows:
|
|
|
|
def __init__(self, goos: str, goarch: str):
|
2021-05-05 12:32:45 +02:00
|
|
|
self._ext = ".exe" if goos == "windows" else ""
|
|
|
|
self._name = os.path.join(".", "CLI", goos, goarch, "miniooni" + self._ext)
|
|
|
|
self._os = goos
|
|
|
|
self._arch = goarch
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-29 19:24:25 +02:00
|
|
|
return
|
|
|
|
ooprivate = OONIProbePrivate()
|
|
|
|
ooprivate.build(engine, options)
|
|
|
|
gogo = SDKGolangGo()
|
|
|
|
gogo.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-29 19:24:25 +02:00
|
|
|
ooprivate.copyfiles(engine, options)
|
|
|
|
cmdline = [
|
|
|
|
"go",
|
|
|
|
"build",
|
|
|
|
"-o",
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name,
|
2021-04-29 19:24:25 +02:00
|
|
|
"-ldflags=-s -w",
|
|
|
|
]
|
|
|
|
if options.debugging():
|
|
|
|
cmdline.append("-x")
|
|
|
|
if options.verbose():
|
|
|
|
cmdline.append("-v")
|
|
|
|
if not options.disable_embedding_psiphon_config():
|
|
|
|
cmdline.append("-tags=ooni_psiphon_config")
|
|
|
|
cmdline.append("./internal/cmd/miniooni")
|
2021-05-05 12:32:45 +02:00
|
|
|
with Environ(engine, "GOOS", self._os):
|
|
|
|
with Environ(engine, "GOARCH", self._arch):
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "CGO_ENABLED", "0"):
|
|
|
|
with AugmentedPath(engine, gogo.binpath()):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
class MiniOONILinux:
|
|
|
|
def __init__(self, goarch: str):
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name = os.path.join(".", "CLI", "linux", goarch, "miniooni")
|
|
|
|
self._arch = goarch
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-04-29 19:24:25 +02:00
|
|
|
return
|
|
|
|
ooprivate = OONIProbePrivate()
|
|
|
|
ooprivate.build(engine, options)
|
|
|
|
gogo = SDKGolangGo()
|
|
|
|
gogo.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-04-29 19:24:25 +02:00
|
|
|
ooprivate.copyfiles(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
if self._arch == "arm":
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "GOARM", "7"):
|
|
|
|
self._build(engine, options, gogo)
|
|
|
|
else:
|
|
|
|
self._build(engine, options, gogo)
|
|
|
|
|
|
|
|
def _build(self, engine: Engine, options: Options, gogo: SDKGolangGo) -> None:
|
2021-04-29 19:24:25 +02:00
|
|
|
cmdline = [
|
|
|
|
"go",
|
|
|
|
"build",
|
|
|
|
"-o",
|
2021-05-05 12:32:45 +02:00
|
|
|
os.path.join("CLI", "linux", self._arch, "miniooni"),
|
2021-04-29 19:24:25 +02:00
|
|
|
"-ldflags=-s -w -extldflags -static",
|
|
|
|
]
|
|
|
|
if options.debugging():
|
|
|
|
cmdline.append("-x")
|
|
|
|
if options.verbose():
|
|
|
|
cmdline.append("-v")
|
|
|
|
tags = "-tags=netgo"
|
|
|
|
if not options.disable_embedding_psiphon_config():
|
|
|
|
tags += ",ooni_psiphon_config"
|
|
|
|
cmdline.append(tags)
|
|
|
|
cmdline.append("./internal/cmd/miniooni")
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "GOOS", "linux"):
|
2021-05-05 12:32:45 +02:00
|
|
|
with Environ(engine, "GOARCH", self._arch):
|
2021-05-05 11:03:48 +02:00
|
|
|
with Environ(engine, "CGO_ENABLED", "0"):
|
|
|
|
with AugmentedPath(engine, gogo.binpath()):
|
2021-05-05 11:33:51 +02:00
|
|
|
engine.require("go")
|
2021-05-05 11:03:48 +02:00
|
|
|
engine.run(cmdline)
|
2021-04-29 19:24:25 +02:00
|
|
|
|
|
|
|
|
2021-05-05 12:12:34 +02:00
|
|
|
# MINIOONI_TARGETS contains all miniooni targets
|
|
|
|
MINIOONI_TARGETS: List[Target] = [
|
|
|
|
MiniOONIDarwinOrWindows("darwin", "amd64"),
|
|
|
|
MiniOONIDarwinOrWindows("darwin", "arm64"),
|
|
|
|
MiniOONILinux("386"),
|
|
|
|
MiniOONILinux("amd64"),
|
|
|
|
MiniOONILinux("arm"),
|
|
|
|
MiniOONILinux("arm64"),
|
|
|
|
MiniOONIDarwinOrWindows("windows", "386"),
|
|
|
|
MiniOONIDarwinOrWindows("windows", "amd64"),
|
|
|
|
]
|
|
|
|
|
2021-05-05 14:26:19 +02:00
|
|
|
# MINIOONI is the top-level "miniooni" target.
|
|
|
|
MINIOONI = Phony("miniooni", MINIOONI_TARGETS)
|
2021-05-05 12:12:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
class OONIProbeLinux:
|
|
|
|
"""OONIProbeLinux builds ooniprobe for Linux."""
|
|
|
|
|
2021-05-05 14:26:19 +02:00
|
|
|
# TODO(bassosimone): this works out of the box on macOS and
|
|
|
|
# requires qemu-user-static on Fedora/Debian. I'm not sure what
|
|
|
|
# is the right (set of) command(s) I should be checking for.
|
|
|
|
|
2021-05-05 12:12:34 +02:00
|
|
|
def __init__(self, goarch: str):
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name = os.path.join(".", "CLI", "linux", goarch, "ooniprobe")
|
|
|
|
self._arch = goarch
|
2021-05-05 12:12:34 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-05-05 12:12:34 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-05-05 12:12:34 +02:00
|
|
|
return
|
|
|
|
ooprivate = OONIProbePrivate()
|
|
|
|
ooprivate.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-05-05 12:12:34 +02:00
|
|
|
ooprivate.copyfiles(engine, options)
|
|
|
|
engine.require("docker")
|
|
|
|
# make sure we have the latest version of the container image
|
|
|
|
engine.run(
|
|
|
|
[
|
|
|
|
"docker",
|
|
|
|
"pull",
|
|
|
|
"--platform",
|
2021-05-05 12:32:45 +02:00
|
|
|
"linux/{}".format(self._arch),
|
2021-05-05 12:12:34 +02:00
|
|
|
"golang:{}-alpine".format(goversion()),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
# then run the build inside the container
|
|
|
|
cmdline = [
|
|
|
|
"docker",
|
|
|
|
"run",
|
|
|
|
"--platform",
|
2021-05-05 12:32:45 +02:00
|
|
|
"linux/{}".format(self._arch),
|
2021-05-05 12:12:34 +02:00
|
|
|
"-e",
|
2021-05-05 12:32:45 +02:00
|
|
|
"GOARCH={}".format(self._arch),
|
2021-05-05 12:12:34 +02:00
|
|
|
"-v",
|
|
|
|
"{}:/ooni".format(os.getcwd()),
|
|
|
|
"-w",
|
|
|
|
"/ooni",
|
|
|
|
"golang:{}-alpine".format(goversion()),
|
|
|
|
os.path.join(".", "CLI", "linux", "build"),
|
|
|
|
]
|
|
|
|
if options.debugging():
|
|
|
|
cmdline.append("-x")
|
|
|
|
if options.verbose():
|
|
|
|
cmdline.append("-v")
|
|
|
|
if not options.disable_embedding_psiphon_config():
|
|
|
|
cmdline.append("-tags=ooni_psiphon_config,netgo")
|
|
|
|
else:
|
|
|
|
cmdline.append("-tags=netgo")
|
|
|
|
engine.run(cmdline)
|
|
|
|
|
|
|
|
|
|
|
|
class OONIProbeWindows:
|
|
|
|
"""OONIProbeWindows builds ooniprobe for Windows."""
|
|
|
|
|
|
|
|
def __init__(self, goarch: str):
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name = os.path.join(".", "CLI", "windows", goarch, "ooniprobe.exe")
|
|
|
|
self._arch = goarch
|
2021-05-05 12:12:34 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-05-05 12:12:34 +02:00
|
|
|
|
|
|
|
def _gcc(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
if self._arch == "amd64":
|
2021-05-05 12:12:34 +02:00
|
|
|
return "x86_64-w64-mingw32-gcc"
|
2021-05-05 12:32:45 +02:00
|
|
|
if self._arch == "386":
|
2021-05-05 12:12:34 +02:00
|
|
|
return "i686-w64-mingw32-gcc"
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-05-05 12:12:34 +02:00
|
|
|
return
|
|
|
|
ooprivate = OONIProbePrivate()
|
|
|
|
ooprivate.build(engine, options)
|
|
|
|
gogo = SDKGolangGo()
|
|
|
|
gogo.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-05-05 12:12:34 +02:00
|
|
|
ooprivate.copyfiles(engine, options)
|
|
|
|
cmdline = [
|
|
|
|
"go",
|
|
|
|
"build",
|
|
|
|
"-o",
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name,
|
2021-05-05 12:12:34 +02:00
|
|
|
"-ldflags=-s -w",
|
|
|
|
]
|
|
|
|
if options.debugging():
|
|
|
|
cmdline.append("-x")
|
|
|
|
if options.verbose():
|
|
|
|
cmdline.append("-v")
|
|
|
|
if not options.disable_embedding_psiphon_config():
|
|
|
|
cmdline.append("-tags=ooni_psiphon_config")
|
|
|
|
cmdline.append("./cmd/ooniprobe")
|
|
|
|
with Environ(engine, "GOOS", "windows"):
|
2021-05-05 12:32:45 +02:00
|
|
|
with Environ(engine, "GOARCH", self._arch):
|
2021-05-05 12:12:34 +02:00
|
|
|
with Environ(engine, "CGO_ENABLED", "1"):
|
|
|
|
with Environ(engine, "CC", self._gcc()):
|
|
|
|
with AugmentedPath(engine, gogo.binpath()):
|
|
|
|
engine.require(self._gcc(), "go")
|
|
|
|
engine.run(cmdline)
|
|
|
|
|
|
|
|
|
|
|
|
class OONIProbeDarwin:
|
|
|
|
"""OONIProbeDarwin builds ooniprobe for macOS."""
|
|
|
|
|
|
|
|
def __init__(self, goarch: str):
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name = os.path.join(".", "CLI", "darwin", goarch, "ooniprobe")
|
|
|
|
self._arch = goarch
|
2021-05-05 12:12:34 +02:00
|
|
|
|
|
|
|
def name(self) -> str:
|
2021-05-05 12:32:45 +02:00
|
|
|
return self._name
|
2021-05-05 12:12:34 +02:00
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
2021-05-05 12:32:45 +02:00
|
|
|
if os.path.isfile(self._name) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self._name))
|
2021-05-05 12:12:34 +02:00
|
|
|
return
|
|
|
|
ooprivate = OONIProbePrivate()
|
|
|
|
ooprivate.build(engine, options)
|
|
|
|
gogo = SDKGolangGo()
|
|
|
|
gogo.build(engine, options)
|
2021-05-05 12:32:45 +02:00
|
|
|
log("\n./make: building {}...".format(self._name))
|
2021-05-05 12:12:34 +02:00
|
|
|
ooprivate.copyfiles(engine, options)
|
|
|
|
cmdline = [
|
|
|
|
"go",
|
|
|
|
"build",
|
|
|
|
"-o",
|
2021-05-05 12:32:45 +02:00
|
|
|
self._name,
|
2021-05-05 12:12:34 +02:00
|
|
|
"-ldflags=-s -w",
|
|
|
|
]
|
|
|
|
if options.debugging():
|
|
|
|
cmdline.append("-x")
|
|
|
|
if options.verbose():
|
|
|
|
cmdline.append("-v")
|
|
|
|
if not options.disable_embedding_psiphon_config():
|
|
|
|
cmdline.append("-tags=ooni_psiphon_config")
|
|
|
|
cmdline.append("./cmd/ooniprobe")
|
|
|
|
with Environ(engine, "GOOS", "darwin"):
|
2021-05-05 12:32:45 +02:00
|
|
|
with Environ(engine, "GOARCH", self._arch):
|
2021-05-05 12:12:34 +02:00
|
|
|
with Environ(engine, "CGO_ENABLED", "1"):
|
|
|
|
with AugmentedPath(engine, gogo.binpath()):
|
|
|
|
engine.require("gcc", "go")
|
|
|
|
engine.run(cmdline)
|
|
|
|
|
|
|
|
|
2021-05-05 14:26:19 +02:00
|
|
|
class Sign:
|
|
|
|
"""Sign signs a specific target artefact."""
|
|
|
|
|
|
|
|
def __init__(self, target: Target):
|
|
|
|
self._target = target
|
|
|
|
|
|
|
|
def name(self) -> str:
|
|
|
|
return self._target.name() + ".asc"
|
|
|
|
|
|
|
|
def build(self, engine: Engine, options: Options) -> None:
|
|
|
|
if os.path.isfile(self.name()) and not options.dry_run():
|
|
|
|
log("\n./make: {}: already built".format(self.name()))
|
|
|
|
return
|
|
|
|
self._target.build(engine, options)
|
|
|
|
log("\n./make: building {}...".format(self.name()))
|
|
|
|
sign(engine, self._target.name())
|
|
|
|
|
|
|
|
|
2021-05-05 12:12:34 +02:00
|
|
|
# OONIPROBE_TARGETS contains all the ooniprobe targets
|
|
|
|
OONIPROBE_TARGETS: List[Target] = [
|
|
|
|
OONIProbeDarwin("amd64"),
|
|
|
|
OONIProbeDarwin("arm64"),
|
|
|
|
OONIProbeLinux("amd64"),
|
|
|
|
OONIProbeLinux("arm64"),
|
|
|
|
OONIProbeWindows("amd64"),
|
|
|
|
OONIProbeWindows("386"),
|
|
|
|
]
|
|
|
|
|
2021-05-05 14:26:19 +02:00
|
|
|
# OONIPROBE_SIGNED_TARGETS contains all the signed ooniprobe targets
|
|
|
|
OONIPROBE_SIGNED_TARGETS: List[Target] = [Sign(x) for x in OONIPROBE_TARGETS]
|
|
|
|
|
|
|
|
# OONIPROBE_RELEASE_DARWIN contains the release darwin targets
|
|
|
|
OONIPROBE_RELEASE_DARWIN = Phony("ooniprobe_release_darwin", [
|
|
|
|
Sign(OONIProbeDarwin("amd64")),
|
|
|
|
Sign(OONIProbeDarwin("arm64")),
|
|
|
|
])
|
|
|
|
|
|
|
|
# OONIPROBE_RELEASE_LINUX contains the release linux targets
|
|
|
|
OONIPROBE_RELEASE_LINUX = Phony("ooniprobe_release_linux", [
|
|
|
|
Sign(OONIProbeLinux("amd64")),
|
|
|
|
Sign(OONIProbeLinux("arm64")),
|
|
|
|
])
|
|
|
|
|
|
|
|
# OONIPROBE_RELEASE_WINDOWS contains the release windows targets
|
|
|
|
OONIPROBE_RELEASE_WINDOWS = Phony("ooniprobe_release_windows", [
|
|
|
|
Sign(OONIProbeWindows("amd64")),
|
|
|
|
Sign(OONIProbeWindows("386")),
|
|
|
|
])
|
|
|
|
|
2021-05-05 12:12:34 +02:00
|
|
|
# MOBILE_TARGETS contains the top-level mobile targets.
|
|
|
|
MOBILE_TARGETS: List[Target] = [
|
2021-05-05 14:26:19 +02:00
|
|
|
ANDROID,
|
|
|
|
IOS,
|
2021-05-05 12:12:34 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
# EXTRA_TARGETS contains extra top-level targets.
|
|
|
|
EXTRA_TARGETS: List[Target] = [
|
2021-05-05 14:26:19 +02:00
|
|
|
MINIOONI,
|
2021-04-27 16:36:57 +02:00
|
|
|
OONIMKAllAAR(),
|
2021-04-29 11:55:30 +02:00
|
|
|
OONIMKAllFrameworkZip(),
|
2021-04-27 16:36:57 +02:00
|
|
|
]
|
|
|
|
|
2021-05-05 12:12:34 +02:00
|
|
|
# VISIBLE_TARGETS contains all the visible-from-CLI targets
|
|
|
|
VISIBLE_TARGETS: List[Target] = (
|
2021-05-05 14:26:19 +02:00
|
|
|
OONIPROBE_TARGETS
|
|
|
|
+ OONIPROBE_SIGNED_TARGETS
|
|
|
|
+ MOBILE_TARGETS
|
|
|
|
+ EXTRA_TARGETS
|
|
|
|
+ MINIOONI_TARGETS
|
|
|
|
+ [OONIPROBE_RELEASE_DARWIN]
|
|
|
|
+ [OONIPROBE_RELEASE_LINUX]
|
|
|
|
+ [OONIPROBE_RELEASE_WINDOWS]
|
2021-05-05 12:12:34 +02:00
|
|
|
)
|
|
|
|
|
2021-04-27 16:36:57 +02:00
|
|
|
|
|
|
|
def main() -> None:
|
|
|
|
"""main function"""
|
2021-05-05 12:12:34 +02:00
|
|
|
toptargets: Dict[str, Target] = dict((t.name(), t) for t in VISIBLE_TARGETS)
|
2021-04-29 19:24:25 +02:00
|
|
|
options = ConfigFromCLI.parse(list(toptargets.keys()))
|
2021-04-27 16:36:57 +02:00
|
|
|
engine = new_engine(options)
|
|
|
|
# note that we check whether the target is known in parse()
|
2021-04-29 19:24:25 +02:00
|
|
|
selected = toptargets[options.target()]
|
2021-04-27 16:36:57 +02:00
|
|
|
selected.build(engine, options)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|