feat(make): implement iOS build (#316)
Part of https://github.com/ooni/probe/issues/1440. Basically, let us continue to update our build infrastructure so that we can release v3.10.0-beta. Now, it's the turn of iOS.
This commit is contained in:
parent
a88d2f35a8
commit
77973301ac
13
.github/workflows/android.yml
vendored
13
.github/workflows/android.yml
vendored
|
@ -4,18 +4,9 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- mobile-staging
|
- mobile-staging
|
||||||
- "release/**"
|
- "release/**"
|
||||||
- "**android**"
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: macos-latest
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v1
|
|
||||||
with:
|
|
||||||
go-version: "1.16"
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: brew install --cask android-sdk
|
- run: ./make --disable-embedding-psiphon-config -t ./MOBILE/android/oonimkall.aar
|
||||||
- run: echo y | sdkmanager --install "platforms;android-29"
|
|
||||||
- run: echo y | sdkmanager --install "ndk-bundle"
|
|
||||||
- run: ./build-android.bash
|
|
||||||
env:
|
|
||||||
ANDROID_HOME: /usr/local/Caskroom/android-sdk/4333796
|
|
||||||
|
|
7
.github/workflows/ios.yml
vendored
7
.github/workflows/ios.yml
vendored
|
@ -6,10 +6,7 @@ on:
|
||||||
- 'release/**'
|
- 'release/**'
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: macos-latest
|
runs-on: macos-10.15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v1
|
|
||||||
with:
|
|
||||||
go-version: "1.16"
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: ./build-ios.bash
|
- run: ./make --disable-embedding-psiphon-config -t ios
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
/*.jsonl
|
/*.jsonl
|
||||||
/*.tar.gz
|
/*.tar.gz
|
||||||
/*.zip
|
/*.zip
|
||||||
|
/.vscode
|
||||||
/apitool
|
/apitool
|
||||||
/apitool.exe
|
/apitool.exe
|
||||||
/coverage.cov
|
/coverage.cov
|
||||||
|
|
224
make
224
make
|
@ -129,6 +129,11 @@ class Options(Protocol):
|
||||||
def debugging(self) -> bool:
|
def debugging(self) -> bool:
|
||||||
"""debugging indicates whether to pass -x to `go build...`."""
|
"""debugging indicates whether to pass -x to `go build...`."""
|
||||||
|
|
||||||
|
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."""
|
||||||
|
|
||||||
def dry_run(self) -> bool:
|
def dry_run(self) -> bool:
|
||||||
"""dry_run indicates whether to execute commands."""
|
"""dry_run indicates whether to execute commands."""
|
||||||
|
|
||||||
|
@ -152,6 +157,7 @@ class ConfigFromCLI:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._debugging = False
|
self._debugging = False
|
||||||
|
self._disable_embedding_psiphon_config = False
|
||||||
self._dry_run = False
|
self._dry_run = False
|
||||||
self._target = ""
|
self._target = ""
|
||||||
self._verbose = False
|
self._verbose = False
|
||||||
|
@ -159,6 +165,9 @@ class ConfigFromCLI:
|
||||||
def debugging(self) -> bool:
|
def debugging(self) -> bool:
|
||||||
return self._debugging
|
return self._debugging
|
||||||
|
|
||||||
|
def disable_embedding_psiphon_config(self) -> bool:
|
||||||
|
return self._disable_embedding_psiphon_config
|
||||||
|
|
||||||
def dry_run(self) -> bool:
|
def dry_run(self) -> bool:
|
||||||
return self._dry_run
|
return self._dry_run
|
||||||
|
|
||||||
|
@ -173,7 +182,7 @@ class ConfigFromCLI:
|
||||||
# could be obtained quite likely w/ argparse.)
|
# could be obtained quite likely w/ argparse.)
|
||||||
|
|
||||||
__usage_string = """\
|
__usage_string = """\
|
||||||
usage: ./make [-nvx] -t target
|
usage: ./make [--disable-embedding-psiphon-config] [-nvx] -t target
|
||||||
./make -l
|
./make -l
|
||||||
./make [--help|-h]
|
./make [--help|-h]
|
||||||
|
|
||||||
|
@ -181,7 +190,11 @@ The first form of the command builds the `target` specified using the
|
||||||
`-t` command line flag. If the target has dependencies, this command will
|
`-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
|
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
|
the command only prints the commands it would run. The `-v` and `-x` flags
|
||||||
are passed directly to `go build ...` and `gomobile bind ...`.
|
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.
|
||||||
|
|
||||||
The second form of the command lists all the available targets as
|
The second form of the command lists all the available targets as
|
||||||
a pretty-printed JSON list.
|
a pretty-printed JSON list.
|
||||||
|
@ -198,12 +211,17 @@ The third form of the command prints this help screen.
|
||||||
|
|
||||||
def _parse(self, targets: List[str]):
|
def _parse(self, targets: List[str]):
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(sys.argv[1:], "hlnt:vx", ["help"])
|
opts, args = getopt.getopt(
|
||||||
|
sys.argv[1:], "hlnt:vx", ["disable-embedding-psiphon-config", "help"]
|
||||||
|
)
|
||||||
except getopt.GetoptError as err:
|
except getopt.GetoptError as err:
|
||||||
self._usage(err=err.msg, exitcode=1)
|
self._usage(err=err.msg, exitcode=1)
|
||||||
if args:
|
if args:
|
||||||
self._usage(err="unexpected number of positional arguments", exitcode=1)
|
self._usage(err="unexpected number of positional arguments", exitcode=1)
|
||||||
for key, value in opts:
|
for key, value in opts:
|
||||||
|
if key == "--disable-embedding-psiphon-config":
|
||||||
|
self._disable_embedding_psiphon_config = True
|
||||||
|
continue
|
||||||
if key in ("-h", "--help"):
|
if key in ("-h", "--help"):
|
||||||
self._usage()
|
self._usage()
|
||||||
if key == "-l":
|
if key == "-l":
|
||||||
|
@ -406,6 +424,9 @@ class SDKGolangGo:
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self.__name
|
return self.__name
|
||||||
|
|
||||||
|
def binpath(self) -> str:
|
||||||
|
return os.path.join(self.__name, "go", "bin")
|
||||||
|
|
||||||
def build(self, engine: Engine, options: Options) -> None:
|
def build(self, engine: Engine, options: Options) -> None:
|
||||||
if os.path.isdir(self.__name) and not options.dry_run():
|
if os.path.isdir(self.__name) and not options.dry_run():
|
||||||
log("./make: {}: already built".format(self.__name))
|
log("./make: {}: already built".format(self.__name))
|
||||||
|
@ -539,8 +560,11 @@ class OONIProbePrivate:
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self.__name
|
return self.__name
|
||||||
|
|
||||||
def copyfiles(self, engine: Engine) -> None:
|
def copyfiles(self, engine: Engine, options: Options) -> None:
|
||||||
"""copyfiles copies psiphon config to the repository."""
|
"""copyfiles copies psiphon config to the repository."""
|
||||||
|
if options.disable_embedding_psiphon_config():
|
||||||
|
log("./make: copy psiphon config: disabled by command line flags")
|
||||||
|
return
|
||||||
engine.run(
|
engine.run(
|
||||||
[
|
[
|
||||||
"cp",
|
"cp",
|
||||||
|
@ -560,6 +584,9 @@ class OONIProbePrivate:
|
||||||
if os.path.isdir(self.__name) and not options.dry_run():
|
if os.path.isdir(self.__name) and not options.dry_run():
|
||||||
log("./make: {}: already built".format(self.__name))
|
log("./make: {}: already built".format(self.__name))
|
||||||
return
|
return
|
||||||
|
if options.disable_embedding_psiphon_config():
|
||||||
|
log("./make: {}: disabled by command line flags".format(self.__name))
|
||||||
|
return
|
||||||
log("./make: building {}...".format(self.__name))
|
log("./make: building {}...".format(self.__name))
|
||||||
engine.require("git", "cp")
|
engine.require("git", "cp")
|
||||||
engine.run(
|
engine.run(
|
||||||
|
@ -597,7 +624,7 @@ class OONIMKAllAAR:
|
||||||
android = SDKAndroid()
|
android = SDKAndroid()
|
||||||
android.build(engine, options)
|
android.build(engine, options)
|
||||||
log("./make: building {}...".format(self.__name))
|
log("./make: building {}...".format(self.__name))
|
||||||
ooprivate.copyfiles(engine)
|
ooprivate.copyfiles(engine, options)
|
||||||
engine.require("sh", "javac")
|
engine.require("sh", "javac")
|
||||||
self._go_get_gomobile(engine, options, oonigo)
|
self._go_get_gomobile(engine, options, oonigo)
|
||||||
self._gomobile_init(engine, oonigo, android)
|
self._gomobile_init(engine, oonigo, android)
|
||||||
|
@ -614,7 +641,7 @@ class OONIMKAllAAR:
|
||||||
# TODO(bassosimone): find a way to run this command without
|
# TODO(bassosimone): find a way to run this command without
|
||||||
# adding extra dependencies to go.mod and go.sum.
|
# adding extra dependencies to go.mod and go.sum.
|
||||||
cmdline: List[str] = []
|
cmdline: List[str] = []
|
||||||
cmdline.append(os.path.join(".", "MOBILE", "android", "go"))
|
cmdline.append(os.path.join(".", "MOBILE", "go"))
|
||||||
cmdline.append("get")
|
cmdline.append("get")
|
||||||
cmdline.append("-u")
|
cmdline.append("-u")
|
||||||
if options.verbose():
|
if options.verbose():
|
||||||
|
@ -642,7 +669,7 @@ class OONIMKAllAAR:
|
||||||
android: SDKAndroid,
|
android: SDKAndroid,
|
||||||
) -> None:
|
) -> None:
|
||||||
cmdline: List[str] = []
|
cmdline: List[str] = []
|
||||||
cmdline.append(os.path.join(".", "MOBILE", "android", "gomobile"))
|
cmdline.append(os.path.join(".", "MOBILE", "gomobile"))
|
||||||
cmdline.append("init")
|
cmdline.append("init")
|
||||||
engine.run(
|
engine.run(
|
||||||
cmdline,
|
cmdline,
|
||||||
|
@ -667,7 +694,7 @@ class OONIMKAllAAR:
|
||||||
android: SDKAndroid,
|
android: SDKAndroid,
|
||||||
) -> None:
|
) -> None:
|
||||||
cmdline: List[str] = []
|
cmdline: List[str] = []
|
||||||
cmdline.append(os.path.join(".", "MOBILE", "android", "gomobile"))
|
cmdline.append(os.path.join(".", "MOBILE", "gomobile"))
|
||||||
cmdline.append("bind")
|
cmdline.append("bind")
|
||||||
if options.verbose():
|
if options.verbose():
|
||||||
cmdline.append("-v")
|
cmdline.append("-v")
|
||||||
|
@ -677,6 +704,7 @@ class OONIMKAllAAR:
|
||||||
cmdline.append("android")
|
cmdline.append("android")
|
||||||
cmdline.append("-o")
|
cmdline.append("-o")
|
||||||
cmdline.append(self.__name)
|
cmdline.append(self.__name)
|
||||||
|
if not options.disable_embedding_psiphon_config():
|
||||||
cmdline.append("-tags")
|
cmdline.append("-tags")
|
||||||
cmdline.append("ooni_psiphon_config")
|
cmdline.append("ooni_psiphon_config")
|
||||||
cmdline.append("-ldflags")
|
cmdline.append("-ldflags")
|
||||||
|
@ -779,8 +807,188 @@ class Android:
|
||||||
bundlejar.build(engine, options)
|
bundlejar.build(engine, options)
|
||||||
|
|
||||||
|
|
||||||
|
class OONIMKAllFramework:
|
||||||
|
"""OONIMKAllFramework creates ./MOBILE/ios/oonimkall.framework."""
|
||||||
|
|
||||||
|
__name = os.path.join(".", "MOBILE", "ios", "oonimkall.framework")
|
||||||
|
|
||||||
|
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
|
||||||
|
ooprivate = OONIProbePrivate()
|
||||||
|
ooprivate.build(engine, options)
|
||||||
|
gogo = SDKGolangGo()
|
||||||
|
gogo.build(engine, options)
|
||||||
|
log("./make: building {}...".format(self.__name))
|
||||||
|
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] = []
|
||||||
|
cmdline.append(os.path.join(".", "MOBILE", "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(
|
||||||
|
[
|
||||||
|
gogo.binpath(), # so we use this binary
|
||||||
|
os.environ["PATH"], # original path
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"GOPATH": gopath(), # where to install gomobile
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _gomobile_init(
|
||||||
|
self,
|
||||||
|
engine: Engine,
|
||||||
|
gogo: SDKGolangGo,
|
||||||
|
) -> None:
|
||||||
|
cmdline: List[str] = []
|
||||||
|
cmdline.append(os.path.join(".", "MOBILE", "gomobile"))
|
||||||
|
cmdline.append("init")
|
||||||
|
engine.run(
|
||||||
|
cmdline,
|
||||||
|
extra_env={
|
||||||
|
"PATH": os.pathsep.join(
|
||||||
|
[
|
||||||
|
os.path.join(gopath(), "bin"), # for gomobile
|
||||||
|
gogo.binpath(), # for our go fork
|
||||||
|
os.environ["PATH"], # original environment
|
||||||
|
]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _gomobile_bind(
|
||||||
|
self,
|
||||||
|
engine: Engine,
|
||||||
|
options: Options,
|
||||||
|
gogo: SDKGolangGo,
|
||||||
|
) -> None:
|
||||||
|
cmdline: List[str] = []
|
||||||
|
cmdline.append(os.path.join(".", "MOBILE", "gomobile"))
|
||||||
|
cmdline.append("bind")
|
||||||
|
if options.verbose():
|
||||||
|
cmdline.append("-v")
|
||||||
|
if options.debugging():
|
||||||
|
cmdline.append("-x")
|
||||||
|
cmdline.append("-target")
|
||||||
|
cmdline.append("ios")
|
||||||
|
cmdline.append("-o")
|
||||||
|
cmdline.append(self.__name)
|
||||||
|
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")
|
||||||
|
engine.run(
|
||||||
|
cmdline,
|
||||||
|
extra_env={
|
||||||
|
"PATH": os.pathsep.join(
|
||||||
|
[
|
||||||
|
os.path.join(gopath(), "bin"), # for gomobile
|
||||||
|
gogo.binpath(), # for our go fork
|
||||||
|
os.environ["PATH"], # original environment
|
||||||
|
]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OONIMKAllFrameworkZip:
|
||||||
|
"""OONIMKAllFrameworkZip creates ./MOBILE/ios/oonimkall.framework.zip."""
|
||||||
|
|
||||||
|
__name = os.path.join(".", "MOBILE", "ios", "oonimkall.framework.zip")
|
||||||
|
|
||||||
|
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
|
||||||
|
engine.require("zip", "rm")
|
||||||
|
ooframework = OONIMKAllFramework()
|
||||||
|
ooframework.build(engine, options)
|
||||||
|
log("./make: building {}...".format(self.__name))
|
||||||
|
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."""
|
||||||
|
|
||||||
|
__name = os.path.join(".", "MOBILE", "ios", "oonimkall.podspec")
|
||||||
|
|
||||||
|
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
|
||||||
|
version = datetime.datetime.now().strftime("%Y.%m.%d-%H%M%S")
|
||||||
|
engine.cat_sed_redirect(
|
||||||
|
"@VERSION@",
|
||||||
|
version,
|
||||||
|
os.path.join(".", "MOBILE", "template.podspec"),
|
||||||
|
self.__name,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class iOS:
|
||||||
|
"""iOS is the toplevel ios target."""
|
||||||
|
|
||||||
|
def name(self) -> str:
|
||||||
|
return "ios"
|
||||||
|
|
||||||
|
def build(self, engine: Engine, options: Options) -> None:
|
||||||
|
ooframeworkzip = OONIMKAllFrameworkZip()
|
||||||
|
ooframeworkzip.build(engine, options)
|
||||||
|
oopodspec = OONIMKAllPodspec()
|
||||||
|
oopodspec.build(engine, options)
|
||||||
|
|
||||||
|
|
||||||
TARGETS: List[Target] = [
|
TARGETS: List[Target] = [
|
||||||
Android(),
|
Android(),
|
||||||
|
iOS(),
|
||||||
BundleJAR(),
|
BundleJAR(),
|
||||||
OONIMKAllAAR(),
|
OONIMKAllAAR(),
|
||||||
OONIProbePrivate(),
|
OONIProbePrivate(),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user