feat(make): build miniooni (#322)

This change is useful to move forward with blessing a new
release (https://github.com/ooni/probe/issues/1439).
This commit is contained in:
Simone Basso 2021-04-29 19:24:25 +02:00 committed by GitHub
parent 9d5a3321af
commit 764293795e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 209 additions and 65 deletions

View File

@ -1,8 +1,10 @@
# android verifies we can still build for Android
name: android name: android
on: on:
push: push:
branches: branches:
- "release/**" - "release/**"
- "master"
jobs: jobs:
test: test:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

View File

@ -1,8 +1,10 @@
# ios verifies we can still build for iOS
name: ios name: ios
on: on:
push: push:
branches: branches:
- 'release/**' - 'release/**'
- 'master'
jobs: jobs:
test: test:
runs-on: macos-10.15 runs-on: macos-10.15

View File

@ -1,47 +1,33 @@
# miniooni checks whether we can build the research client miniooni
# and publishes all linux binaries as artefacts. There is no point in
# publishing windows or darwin binaries b/c they are not signed.
name: miniooni name: miniooni
on: on:
push: push:
branches:
- 'release/**'
schedule:
- cron: "0 0 * * */1"
jobs: jobs:
test: test:
runs-on: ubuntu-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: ./make --disable-embedding-psiphon-config -t miniooni
- run: ./build-miniooni.sh linux
- run: ./CLI/linux/amd64/miniooni --yes -nNi https://example.com web_connectivity - run: ./CLI/linux/amd64/miniooni --yes -nNi https://example.com web_connectivity
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: miniooni-linux-386 name: miniooni-linux-386
path: ./CLI/linux/386/miniooni path: ./CLI/linux/386/miniooni
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: miniooni-linux-amd64 name: miniooni-linux-amd64
path: ./CLI/linux/amd64/miniooni path: ./CLI/linux/amd64/miniooni
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: miniooni-linux-arm name: miniooni-linux-arm
path: ./CLI/linux/arm/miniooni path: ./CLI/linux/arm/miniooni
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: miniooni-linux-arm64 name: miniooni-linux-arm64
path: ./CLI/linux/arm64/miniooni path: ./CLI/linux/arm64/miniooni
- run: ./build-miniooni.sh darwin
- uses: actions/upload-artifact@v1
with:
name: miniooni-darwin-amd64
path: ./CLI/darwin/amd64/miniooni
- run: sudo apt install --yes mingw-w64
- run: ./build-miniooni.sh windows
- uses: actions/upload-artifact@v1
with:
name: miniooni-windows-amd64.exe
path: ./CLI/windows/amd64/miniooni.exe

2
CLI/darwin/arm64/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/ooniprobe
/miniooni

View File

@ -21,7 +21,7 @@ Please, make sure you tag such issues using the `ooni/probe-cli` label.
Every top-level directory contains an explanatory README file. Every top-level directory contains an explanatory README file.
## Development setup ## OONIProbe
Be sure you have golang >= 1.16 and a C compiler (when developing for Windows, you Be sure you have golang >= 1.16 and a C compiler (when developing for Windows, you
need Mingw-w64 installed). You can build using: need Mingw-w64 installed). You can build using:
@ -34,6 +34,8 @@ This will generate a binary called `ooniprobe` in the current directory.
## Android bindings ## Android bindings
Make sure you have Python 3.8+ installed, then run:
```bash ```bash
./make -t android ./make -t android
``` ```
@ -47,6 +49,8 @@ are published along with the release notes.
## iOS bindings ## iOS bindings
Make sure you have Python 3.8+ installed, then run:
```bash ```bash
./make -t ios ./make -t ios
``` ```
@ -57,6 +61,16 @@ cannot clone private repositories in the https://github.com/ooni namespace.)
The generated bindings are (manually) added to GitHub releases. The instructions The generated bindings are (manually) added to GitHub releases. The instructions
explaining how to integrate these bindings are published along with the release notes. explaining how to integrate these bindings are published along with the release notes.
## miniooni
Miniooni is the experimental OONI client used for research. Compile using:
```bash
go build -v ./internal/cmd/miniooni
```
This will generate a binary called `miniooni` in the current directory.
## Updating dependencies ## Updating dependencies
```bash ```bash
@ -68,3 +82,5 @@ go get -u -v ./... && go mod tidy
Create an issue according to [the routine release template]( Create an issue according to [the routine release template](
https://github.com/ooni/probe/blob/master/.github/ISSUE_TEMPLATE/routine-sprint-releases.md) https://github.com/ooni/probe/blob/master/.github/ISSUE_TEMPLATE/routine-sprint-releases.md)
and perform any item inside the check-list. and perform any item inside the check-list.
We build releases using `./make`, which requires Python3.8+.

View File

@ -1,31 +0,0 @@
#!/bin/sh
set -e
case $1 in
macos|darwin)
export GOOS=darwin GOARCH=amd64
go build -o ./CLI/darwin/amd64 -ldflags="-s -w" ./internal/cmd/miniooni
echo "Binary ready at ./CLI/darwin/amd64/miniooni";;
linux)
export GOOS=linux GOARCH=386
go build -o ./CLI/linux/386 -tags netgo -ldflags='-s -w -extldflags "-static"' ./internal/cmd/miniooni
echo "Binary ready at ./CLI/linux/386/miniooni"
export GOOS=linux GOARCH=amd64
go build -o ./CLI/linux/amd64 -tags netgo -ldflags='-s -w -extldflags "-static"' ./internal/cmd/miniooni
echo "Binary ready at ./CLI/linux/amd64/miniooni"
export GOOS=linux GOARCH=arm GOARM=7
go build -o ./CLI/linux/arm -tags netgo -ldflags='-s -w -extldflags "-static"' ./internal/cmd/miniooni
echo "Binary ready at ./CLI/linux/arm/miniooni"
export GOOS=linux GOARCH=arm64
go build -o ./CLI/linux/arm64 -tags netgo -ldflags='-s -w -extldflags "-static"' ./internal/cmd/miniooni
echo "Binary ready at ./CLI/linux/arm64/miniooni";;
windows)
export GOOS=windows GOARCH=386
go build -o ./CLI/windows/386 -ldflags="-s -w" ./internal/cmd/miniooni
echo "Binary ready at ./CLI/windows/386/miniooni.exe"
export GOOS=windows GOARCH=amd64
go build -o ./CLI/windows/amd64 -ldflags="-s -w" ./internal/cmd/miniooni
echo "Binary ready at ./CLI/windows/amd64/miniooni.exe";;
*)
echo "usage: $0 darwin|linux|windows" 1>&2
exit 1
esac

187
make
View File

@ -259,7 +259,7 @@ class Engine(Protocol):
output_variable: str, output_variable: str,
cmdline: List[str], cmdline: List[str],
output: List[bytes], output: List[bytes],
)->None: ) -> None:
"""backticks executes output_variable=`*cmdline`.""" """backticks executes output_variable=`*cmdline`."""
def cat_sed_redirect( def cat_sed_redirect(
@ -283,6 +283,12 @@ class Engine(Protocol):
) -> None: ) -> None:
"""run runs the specified command line.""" """run runs the specified command line."""
def setenv(self, key: str, value: str) -> None:
"""setenv sets an environment variable."""
def unsetenv(self, key: str) -> None:
"""unsetenv clears an environment variable."""
class CommandRealExecutor: class CommandRealExecutor:
"""CommandRealExecutor executes commands.""" """CommandRealExecutor executes commands."""
@ -292,7 +298,7 @@ class CommandRealExecutor:
output_variable: str, output_variable: str,
cmdline: List[str], cmdline: List[str],
output: List[bytes], output: List[bytes],
)->None: ) -> None:
"""backticks implements Engine.backticks""" """backticks implements Engine.backticks"""
# Implemented in CommandDryRunner # Implemented in CommandDryRunner
@ -331,6 +337,14 @@ class CommandRealExecutor:
env[key] = value env[key] = value
subprocess.run(cmdline, check=True, cwd=cwd, env=env, input=inputbytes) subprocess.run(cmdline, check=True, cwd=cwd, env=env, input=inputbytes)
def setenv(self, key: str, value: str) -> None:
"""setenv implements Engine.setenv."""
os.environ[key] = value
def unsetenv(self, key: str) -> None:
"""unsetenv implements Engine.unsetenv."""
del os.environ[key]
class CommandDryRunner: class CommandDryRunner:
"""CommandDryRunner is the dry runner.""" """CommandDryRunner is the dry runner."""
@ -346,9 +360,9 @@ class CommandDryRunner:
output_variable: str, output_variable: str,
cmdline: List[str], cmdline: List[str],
output: List[bytes], output: List[bytes],
)->None: ) -> None:
"""backticks implements Engine.backticks""" """backticks implements Engine.backticks"""
log('./make: {}=`{}`'.format(output_variable, shlex.join(cmdline))) log("./make: {}=`{}`".format(output_variable, shlex.join(cmdline)))
# implemented here because we want to see the result of backticks # implemented here because we want to see the result of backticks
# command invocations when we're doing a dry run # command invocations when we're doing a dry run
popen = subprocess.Popen(cmdline, stdout=subprocess.PIPE) popen = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
@ -402,6 +416,14 @@ class CommandDryRunner:
envpart += shlex.join(["{}={}".format(key, value)]) + " " envpart += shlex.join(["{}={}".format(key, value)]) + " "
log("./make: {}{}{}".format(cdpart, envpart, shlex.join(cmdline))) log("./make: {}{}{}".format(cdpart, envpart, shlex.join(cmdline)))
def setenv(self, key: str, value: str) -> None:
"""setenv implements Engine.setenv."""
log("./make: export {}={}".format(key, shlex.join([value])))
def unsetenv(self, key: str) -> None:
"""unsetenv implements Engine.unsetenv."""
log("./make: unset {}".format(key))
class EngineComposer: class EngineComposer:
"""EngineComposer composes two engines.""" """EngineComposer composes two engines."""
@ -415,7 +437,7 @@ class EngineComposer:
output_variable: str, output_variable: str,
cmdline: List[str], cmdline: List[str],
output: List[bytes], output: List[bytes],
)->None: ) -> None:
"""backticks implements Engine.backticks""" """backticks implements Engine.backticks"""
self._first.backticks(output_variable, cmdline, output) self._first.backticks(output_variable, cmdline, output)
self._second.backticks(output_variable, cmdline, output) self._second.backticks(output_variable, cmdline, output)
@ -448,6 +470,16 @@ class EngineComposer:
self._first.run(cmdline, cwd=cwd, extra_env=extra_env, inputbytes=inputbytes) self._first.run(cmdline, cwd=cwd, extra_env=extra_env, inputbytes=inputbytes)
self._second.run(cmdline, cwd=cwd, extra_env=extra_env, inputbytes=inputbytes) self._second.run(cmdline, cwd=cwd, extra_env=extra_env, inputbytes=inputbytes)
def setenv(self, key: str, value: str) -> None:
"""setenv implements Engine.setenv."""
self._first.setenv(key, value)
self._second.setenv(key, value)
def unsetenv(self, key: str) -> None:
"""unsetenv implements Engine.unsetenv."""
self._first.unsetenv(key)
self._second.unsetenv(key)
def new_engine(options: Options) -> Engine: def new_engine(options: Options) -> Engine:
"""new_engine creates a new engine instance""" """new_engine creates a new engine instance"""
@ -816,7 +848,9 @@ class BundleJAR:
] ]
) )
engine.cat_sed_redirect( engine.cat_sed_redirect(
[("@VERSION@", version),], [
("@VERSION@", version),
],
os.path.join("MOBILE", "template.pom"), os.path.join("MOBILE", "template.pom"),
os.path.join("MOBILE", "android", "oonimkall-{}.pom".format(version)), os.path.join("MOBILE", "android", "oonimkall-{}.pom".format(version)),
) )
@ -961,7 +995,7 @@ class OONIMKAllFramework:
"PATH": os.pathsep.join( "PATH": os.pathsep.join(
[ [
os.path.join(gopath(), "bin"), # for gomobile os.path.join(gopath(), "bin"), # for gomobile
gogo.binpath(), # for our go fork gogo.binpath(), # for golang/go
os.environ["PATH"], # original environment os.environ["PATH"], # original environment
] ]
), ),
@ -1040,9 +1074,142 @@ class iOS:
oopodspec.build(engine, options) oopodspec.build(engine, options)
class MiniOONIDarwinOrWindows:
def __init__(self, goos: str, goarch: str):
self.__ext = ".exe" if goos == "windows" else ""
self.__name = os.path.join(".", "CLI", goos, goarch, "miniooni" + self.__ext)
self.__os = goos
self.__arch = goarch
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)
engine.setenv("CGO_ENABLED", "0")
engine.setenv("GOOS", self.__os)
engine.setenv("GOARCH", self.__arch)
cmdline = [
"go",
"build",
"-o",
self.__name,
"-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")
engine.run(
cmdline,
extra_env={
"PATH": os.pathsep.join(
[
gogo.binpath(), # for golang/go
os.environ["PATH"], # original path
]
),
},
)
engine.unsetenv("CGO_ENABLED")
engine.unsetenv("GOARCH")
engine.unsetenv("GOOS")
class MiniOONILinux:
def __init__(self, goarch: str):
self.__name = os.path.join(".", "CLI", "linux", goarch, "miniooni")
self.__arch = goarch
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)
engine.setenv("CGO_ENABLED", "0")
engine.setenv("GOOS", "linux")
engine.setenv("GOARCH", self.__arch)
if self.__arch == "arm":
engine.setenv("GOARM", "7")
cmdline = [
"go",
"build",
"-o",
os.path.join("CLI", "linux", self.__arch, "miniooni"),
"-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")
engine.run(
cmdline,
extra_env={
"PATH": os.pathsep.join(
[
gogo.binpath(), # for golang/go
os.environ["PATH"], # original path
]
),
},
)
engine.unsetenv("CGO_ENABLED")
engine.unsetenv("GOARCH")
engine.unsetenv("GOOS")
if self.__arch == "arm":
engine.unsetenv("GOARM")
class MiniOONI:
"""MiniOONI is the top-level 'miniooni' target."""
__name = "miniooni"
def name(self) -> str:
return self.__name
def build(self, engine: Engine, options: Options) -> None:
for builder in (
MiniOONIDarwinOrWindows("darwin", "amd64"),
MiniOONIDarwinOrWindows("darwin", "arm64"),
MiniOONILinux("386"),
MiniOONILinux("amd64"),
MiniOONILinux("arm"),
MiniOONILinux("arm64"),
MiniOONIDarwinOrWindows("windows", "386"),
MiniOONIDarwinOrWindows("windows", "amd64"),
):
builder.build(engine, options)
TARGETS: List[Target] = [ TARGETS: List[Target] = [
Android(), Android(),
iOS(), iOS(),
MiniOONI(),
OONIMKAllAAR(), OONIMKAllAAR(),
OONIMKAllFrameworkZip(), OONIMKAllFrameworkZip(),
] ]
@ -1050,11 +1217,11 @@ TARGETS: List[Target] = [
def main() -> None: def main() -> None:
"""main function""" """main function"""
alltargets: Dict[str, Target] = dict((t.name(), t) for t in TARGETS) toptargets: Dict[str, Target] = dict((t.name(), t) for t in TARGETS)
options = ConfigFromCLI.parse(list(alltargets.keys())) options = ConfigFromCLI.parse(list(toptargets.keys()))
engine = new_engine(options) engine = new_engine(options)
# note that we check whether the target is known in parse() # note that we check whether the target is known in parse()
selected = alltargets[options.target()] selected = toptargets[options.target()]
selected.build(engine, options) selected.build(engine, options)