26 Commits

Author SHA1 Message Date
Joe Julian 1762d36dde gogio: deduplicate Android jars 2026-04-16 21:04:56 -07:00
Joe Julian 7b5a6b418c gogio: load module-root Android jars 2026-04-16 21:03:09 -07:00
Joe Julian f71579e799 gogio: load Android assets from module root 2026-04-16 20:59:20 -07:00
Joe Julian 192acd9d09 Add Android packaging hooks for app resources 2026-04-16 08:42:03 -07:00
inkeliz 2e72e8f0b2 gogio: [android] bump target sdk version
Match the "Target SDK" with the mininum required
by Google Play.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2026-02-28 01:10:27 +00:00
CoyAce f4d6788248 gogio: [ios] add CXX, CGO_CXXFLAGS environment variable for cgo cross-compilation and correct signKey
Signed-off-by: CoyAce <AkeyCoy@gmail.com>
2026-02-10 07:34:11 +01:00
CoyAce e5b1a4e6cd gogio: [android] add CXX environment variable for cgo cross-compilation
Adds the CXX environment variable when building Android shared libraries
to ensure proper C++ compiler selection for cgo-enabled packages.

Signed-off-by: CoyAce <AkeyCoy@gmail.com>
2026-01-26 16:24:59 +01:00
CoyAce f587d2f097 gogio: [Android] map the microphone permission to RECORD_AUDIO
Signed-off-by: CoyAce <AkeyCoy@gmail.com>
2026-01-07 12:34:13 +01:00
inkeliz 8de547d61d gogio: [Android] add support for querying apps
Previously, it was impossible to identify if a specific app
was installed on the user device. It was also impossible to
launch a external app from Gio using Intent.

Now, you can use `-queries` with a comma separed package names,
like `com.another.app`. That allows you to launch Intent to
`com.another.app`.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2025-12-16 21:53:56 +01:00
inkeliz 048614c60e gogio: [iOS] fix compatibility with Apple Connect and iPad requirement
This pach fixes a total of 6 issues caused by gogio,
when uploading .ipa to Apple Connect/Apple Store.

1. Asset validation failed (90474), caused by not set
"UIInterfaceOrientationPortraitUpside" in plist.

2. Asset validation failed (90482), the executable contains
bitcode. Now, gogio will use "bitcode_strip" to remove such
bitcode.

3. Asset validation failed (90060), the version can only have
three non-negative numbers. Using values from semVer is
invalid (such as 1.2.3.4), it must be either 1.2.3 or
1.2.34. Now, gogio uses the later one.

4. Asset validation failed (90476), supporting multitask on
iPad requires UILaunchScreen. That is tricky to solve, instead
gogio will NOT support multitask on iPad.

5. Asset validation failed (90208), version mismatch between
plist and binary. Now, gogio will use compile flags to set
the version AND will use the proper minSdk on plist.

6. Asset validation failed (90023), missing 152x152 icon for
iPad. Now, gogio will create such icon.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-12-16 18:40:23 +01:00
inkeliz ed8d0aa9a6 gogio: [macOS] support custom profile
This patch enables `-signkey` to load provisioning profiles.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-12-16 18:04:09 +01:00
inkeliz e1f06eb7b0 gogio: [wasm] fix compatibility with Go 1.23+
Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2025-12-15 22:06:47 +00:00
inkeliz ae8a780af9 gogio: add deeplink support
Add a new flag "-schemes" which links the URL schemes to the app.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2025-12-15 22:25:08 +01:00
Chris Waldon 74551d3253 go.*: update to gio v0.9.0
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2025-09-16 08:41:55 -04:00
inkeliz aecb4723c9 gogio: [Android] support 16kB page sizes, required for Android 15+
Previously, Gio crashes on 16KB page-size enable version of
Android 15. Also, Google Play will require 16KB compatible
apps by November 2025.

This patch changes the page-size of C/CGO to 64KB, which is
compatible with 4KB, 16KB and 64KB. That is also the same
value used by Golang Compiler itself.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-05-27 10:26:34 +02:00
Elias Naur 3f0ad89ca9 gogio: use ANDROID_HOME instead of the deprecated ANDROID_SDK_ROOT
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-05-24 11:20:00 +02:00
Sean E. Russell c108ce0a29 gogio: read -signpass from environment variable if it isn't provided as an argument
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-05-22 17:36:52 +02:00
Elias Naur 1b42337ac0 gogio: improve -version parse error message
Add a test while here.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-05-22 17:23:53 +02:00
Admin ae8dd5433d all: cleanup code and upgrade to modern Go facilities
Signed-off-by: ddkwork
2025-05-05 19:53:28 +02:00
Admin ab2d621e47 go.*: bump Go and dependencies
Signed-off-by: ddkwork
2025-05-05 19:53:28 +02:00
Elias Naur 01ffdf788d gogio: disable failing JS test
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-03-21 13:07:11 +01:00
Elias Naur 51c6d8037b .builds: upgrade debian image to fix a wine compatibility issue
See https://groups.google.com/g/golang-nuts/c/Msg1USaNaqM

Also disable the Apple builder, it no longer builds.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-03-21 13:07:11 +01:00
Elias Naur d1bccae359 go.mod,.builds: upgrade minimum Go version to 1.23
Remove a redundant package installation line while here.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-03-21 11:00:04 +01:00
Thomas Bruyelle 37612f9112 gogio: fix #633 wasm_exec.js location for go1.24
Signed-off-by: Thomas Bruyelle <thomas.bruyelle@gmail.com>
2025-03-21 10:47:41 +01:00
Chris Waldon 03a1ada8ac go.*: update to gio v0.8.0
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2025-01-14 10:57:56 -05:00
Chris Waldon eb9fbc797e go.*: update to Gio v0.7.1
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2024-07-16 20:39:39 -04:00
23 changed files with 808 additions and 295 deletions
+19 -10
View File
@@ -8,8 +8,10 @@ packages:
- libxml2-dev - libxml2-dev
- libssl-dev - libssl-dev
- libz-dev - libz-dev
- ninja-build # cctools
- llvm-dev # for cctools - llvm-dev # for cctools
- uuid-dev ## for cctools - uuid-dev # for cctools
- libblocksruntime-dev # for cctools
- libplist-utils # for gogio - libplist-utils # for gogio
sources: sources:
- https://git.sr.ht/~eliasnaur/gio-cmd - https://git.sr.ht/~eliasnaur/gio-cmd
@@ -17,6 +19,7 @@ sources:
- https://git.sr.ht/~eliasnaur/giouiorg - https://git.sr.ht/~eliasnaur/giouiorg
- https://github.com/tpoechtrager/cctools-port.git - https://github.com/tpoechtrager/cctools-port.git
- https://github.com/tpoechtrager/apple-libtapi.git - https://github.com/tpoechtrager/apple-libtapi.git
- https://github.com/tpoechtrager/apple-libdispatch
- https://github.com/mackyle/xar.git - https://github.com/mackyle/xar.git
environment: environment:
APPLE_TOOLCHAIN_ROOT: /home/build/appletools APPLE_TOOLCHAIN_ROOT: /home/build/appletools
@@ -24,7 +27,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl -s https://dl.google.com/go/go1.19.8.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl -s https://dl.google.com/go/go1.24.1.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- prepare_toolchain: | - prepare_toolchain: |
mkdir -p $APPLE_TOOLCHAIN_ROOT mkdir -p $APPLE_TOOLCHAIN_ROOT
cd $APPLE_TOOLCHAIN_ROOT cd $APPLE_TOOLCHAIN_ROOT
@@ -42,6 +45,11 @@ tasks:
- install_appletoolchain: | - install_appletoolchain: |
cd giouiorg cd giouiorg
go build -o $APPLE_TOOLCHAIN_ROOT/tools ./cmd/appletoolchain go build -o $APPLE_TOOLCHAIN_ROOT/tools ./cmd/appletoolchain
- build_libdispatch: |
cd apple-libdispatch
cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_INSTALL_PREFIX=$APPLE_TOOLCHAIN_ROOT/libdispatch .
ninja
ninja install
- build_xar: | - build_xar: |
cd xar/xar cd xar/xar
ac_cv_lib_crypto_OpenSSL_add_all_ciphers=yes CC=clang ./autogen.sh --prefix=/usr ac_cv_lib_crypto_OpenSSL_add_all_ciphers=yes CC=clang ./autogen.sh --prefix=/usr
@@ -53,15 +61,16 @@ tasks:
./install.sh ./install.sh
- build_cctools: | - build_cctools: |
cd cctools-port/cctools cd cctools-port/cctools
./configure --prefix $APPLE_TOOLCHAIN_ROOT/toolchain --with-libtapi=$APPLE_TOOLCHAIN_ROOT/libtapi --target=x86_64-apple-darwin19 ./configure --prefix $APPLE_TOOLCHAIN_ROOT/toolchain --with-libtapi=$APPLE_TOOLCHAIN_ROOT/libtapi --with-libdispatch=$APPLE_TOOLCHAIN_ROOT/libdispatch --target=x86_64-apple-darwin19
make install make install
- install_gogio: | - install_gogio: |
cd gio-cmd cd gio-cmd
go install ./gogio go install ./gogio
- test_ios_gogio: | # Broken test.
mkdir tmp # - test_ios_gogio: |
cd tmp # mkdir tmp
go mod init example.com # cd tmp
go get -d gioui.org/example/kitchen # go mod init example.com
export PATH=/home/build/appletools/bin:$PATH # go get -d gioui.org/example/kitchen
gogio -target ios -o app.app gioui.org/example/kitchen # export PATH=/home/build/appletools/bin:$PATH
# gogio -target ios -o app.app gioui.org/example/kitchen
+1 -1
View File
@@ -16,7 +16,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl https://dl.google.com/go/go1.19.8.freebsd-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl https://dl.google.com/go/go1.24.1.freebsd-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- test_cmd: | - test_cmd: |
cd gio-cmd cd gio-cmd
go test ./... go test ./...
+4 -5
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Unlicense OR MIT # SPDX-License-Identifier: Unlicense OR MIT
image: debian/bookworm image: debian/testing
packages: packages:
- curl - curl
- pkg-config - pkg-config
@@ -24,7 +24,6 @@ packages:
- scrot - scrot
- sway - sway
- grim - grim
- wine
- unzip - unzip
sources: sources:
- https://git.sr.ht/~eliasnaur/gio-cmd - https://git.sr.ht/~eliasnaur/gio-cmd
@@ -39,7 +38,7 @@ secrets:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl -s https://dl.google.com/go/go1.19.8.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl -s https://dl.google.com/go/go1.24.1.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- check_gofmt: | - check_gofmt: |
cd gio-cmd cd gio-cmd
test -z "$(gofmt -s -l .)" test -z "$(gofmt -s -l .)"
@@ -57,8 +56,8 @@ tasks:
# mirror to github # mirror to github
ssh-keyscan github.com > "$HOME"/.ssh/known_hosts && cd gio-cmd && git push --mirror "$github_mirror" || echo "failed mirroring" ssh-keyscan github.com > "$HOME"/.ssh/known_hosts && cd gio-cmd && git push --mirror "$github_mirror" || echo "failed mirroring"
- install_chrome: | - install_chrome: |
curl -s https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add - sudo curl -o /etc/apt/keyrings/google.pub -s https://dl.google.com/linux/linux_signing_key.pub
sudo sh -c 'echo "deb [arch=amd64] https://dl-ssl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' sudo sh -c 'echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google.pub] https://dl-ssl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
sudo apt-get -qq update sudo apt-get -qq update
sudo apt-get -qq install -y google-chrome-stable sudo apt-get -qq install -y google-chrome-stable
- test: | - test: |
+1 -1
View File
@@ -10,7 +10,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl https://dl.google.com/go/go1.19.8.src.tar.gz | tar -C /home/build/sdk -xzf - curl https://dl.google.com/go/go1.24.1.src.tar.gz | tar -C /home/build/sdk -xzf -
cd /home/build/sdk/go/src cd /home/build/sdk/go/src
./make.bash ./make.bash
- test_cmd: | - test_cmd: |
+18 -19
View File
@@ -1,29 +1,28 @@
module gioui.org/cmd module gioui.org/cmd
go 1.21 go 1.24.1
require ( require (
gioui.org v0.7.0 gioui.org v0.9.0
github.com/akavel/rsrc v0.10.1 github.com/akavel/rsrc v0.10.1
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4 github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4
github.com/chromedp/chromedp v0.5.2 github.com/chromedp/chromedp v0.13.6
golang.org/x/image v0.5.0 golang.org/x/image v0.26.0
golang.org/x/sync v0.1.0 golang.org/x/sync v0.13.0
golang.org/x/text v0.9.0 golang.org/x/text v0.24.0
golang.org/x/tools v0.6.0 golang.org/x/tools v0.32.0
) )
require ( require (
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect
gioui.org/shader v1.0.8 // indirect gioui.org/shader v1.0.8 // indirect
github.com/go-text/typesetting v0.1.1 // indirect github.com/chromedp/sysutil v1.1.0 // indirect
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect github.com/go-json-experiment/json v0.0.0-20250417205406-170dfdcf87d1 // indirect
github.com/gobwas/pool v0.2.0 // indirect github.com/go-text/typesetting v0.3.0 // indirect
github.com/gobwas/ws v1.0.2 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 // indirect github.com/gobwas/pool v0.2.1 // indirect
github.com/mailru/easyjson v0.7.0 // indirect github.com/gobwas/ws v1.4.0 // indirect
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 // indirect github.com/google/go-cmp v0.7.0 // indirect
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 // indirect golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.24.0 // indirect
golang.org/x/sys v0.5.0 // indirect golang.org/x/sys v0.33.0 // indirect
) )
+43 -63
View File
@@ -1,70 +1,50 @@
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
gioui.org v0.7.0 h1:5I+7Uu2yjTu7W5p7HWQrgsDPO3vex+8T1DsvCLGBfuI= gioui.org v0.9.0 h1:4u7XZwnb5kzQW91Nz/vR0wKD6LdW9CaVF96r3rfy4kc=
gioui.org v0.7.0/go.mod h1:19wZxaNP+eHN4H2YdZwEfbkAAgoYB5rcIbDHo4BqUl4= gioui.org v0.9.0/go.mod h1:CjNig0wAhLt9WZxOPAusgFD8x8IRvqt26LdDBa3Jvao=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA= gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
github.com/akavel/rsrc v0.10.1 h1:hCCPImjmFKVNGpeLZyTDRHEFC283DzyTXTo0cO0Rq9o= github.com/akavel/rsrc v0.10.1 h1:hCCPImjmFKVNGpeLZyTDRHEFC283DzyTXTo0cO0Rq9o=
github.com/akavel/rsrc v0.10.1/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/akavel/rsrc v0.10.1/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4 h1:QD3KxSJ59L2lxG6MXBjNHxiQO2RmxTQ3XcK+wO44WOg= github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 h1:UZdrvid2JFwnvPlUSEFlE794XZL4Jmrj8fuxfcLECJE=
github.com/chromedp/cdproto v0.0.0-20191114225735-6626966fbae4/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/chromedp/chromedp v0.5.2 h1:W8xBXQuUnd2dZK0SN/lyVwsQM7KgW+kY5HGnntms194= github.com/chromedp/chromedp v0.13.6 h1:xlNunMyzS5bu3r/QKrb3fzX6ow3WBQ6oao+J65PGZxk=
github.com/chromedp/chromedp v0.5.2/go.mod h1:rsTo/xRo23KZZwFmWk2Ui79rBaVRRATCjLzNQlOFSiA= github.com/chromedp/chromedp v0.13.6/go.mod h1:h8GPP6ZtLMLsU8zFbTcb7ZDGCvCy8j/vRoFmRltQx9A=
github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= github.com/go-json-experiment/json v0.0.0-20250417205406-170dfdcf87d1 h1:+VexzzkMLb1tnvpuQdGT/DicIRW7MN8ozsXqBMgp0Hk=
github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/go-json-experiment/json v0.0.0-20250417205406-170dfdcf87d1/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+3 -3
View File
@@ -24,9 +24,9 @@ type AndroidTestDriver struct {
var rxAdbDevice = regexp.MustCompile(`(.*)\s+device$`) var rxAdbDevice = regexp.MustCompile(`(.*)\s+device$`)
func (d *AndroidTestDriver) Start(path string) { func (d *AndroidTestDriver) Start(path string) {
d.sdkDir = os.Getenv("ANDROID_SDK_ROOT") d.sdkDir = os.Getenv("ANDROID_HOME")
if d.sdkDir == "" { if d.sdkDir == "" {
d.Skipf("Android SDK is required; set $ANDROID_SDK_ROOT") d.Skipf("Android SDK is required; set $ANDROID_HOME")
} }
d.adbPath = filepath.Join(d.sdkDir, "platform-tools", "adb") d.adbPath = filepath.Join(d.sdkDir, "platform-tools", "adb")
if _, err := os.Stat(d.adbPath); os.IsNotExist(err) { if _, err := os.Stat(d.adbPath); os.IsNotExist(err) {
@@ -121,7 +121,7 @@ func (d *AndroidTestDriver) tryUninstall() {
} }
} }
func (d *AndroidTestDriver) adb(args ...interface{}) []byte { func (d *AndroidTestDriver) adb(args ...any) []byte {
strs := []string{} strs := []string{}
for _, arg := range args { for _, arg := range args {
strs = append(strs, fmt.Sprint(arg)) strs = append(strs, fmt.Sprint(arg))
+195 -68
View File
@@ -40,14 +40,18 @@ type errWriter struct {
var exeSuffix string var exeSuffix string
type manifestData struct { type manifestData struct {
AppID string AppID string
Version Semver Version Semver
MinSDK int MinSDK int
TargetSDK int TargetSDK int
Permissions []string Permissions []string
Features []string Features []string
IconSnip string IconSnip string
AppName string AppName string
Schemes []string
PackageQueries []string
ManifestSnip string
AppSnip string
} }
const ( const (
@@ -76,9 +80,9 @@ func init() {
} }
func buildAndroid(tmpDir string, bi *buildInfo) error { func buildAndroid(tmpDir string, bi *buildInfo) error {
sdk := os.Getenv("ANDROID_SDK_ROOT") sdk := os.Getenv("ANDROID_HOME")
if sdk == "" { if sdk == "" {
return errors.New("please set ANDROID_SDK_ROOT to the Android SDK path") return errors.New("please set ANDROID_HOME to the Android SDK path")
} }
if _, err := os.Stat(sdk); err != nil { if _, err := os.Stat(sdk); err != nil {
return err return err
@@ -113,7 +117,9 @@ func buildAndroid(tmpDir string, bi *buildInfo) error {
if err != nil { if err != nil {
return err return err
} }
moduleRoot := moduleRootForDir(bi.pkgDir)
var extraJars []string var extraJars []string
seenJars := make(map[string]bool)
visitedPkgs := make(map[string]bool) visitedPkgs := make(map[string]bool)
var visitPkg func(*packages.Package) error var visitPkg func(*packages.Package) error
visitPkg = func(p *packages.Package) error { visitPkg = func(p *packages.Package) error {
@@ -121,11 +127,17 @@ func buildAndroid(tmpDir string, bi *buildInfo) error {
return nil return nil
} }
dir := filepath.Dir(p.GoFiles[0]) dir := filepath.Dir(p.GoFiles[0])
jars, err := filepath.Glob(filepath.Join(dir, "*.jar")) jars, err := androidExtraJars(dir, moduleRoot)
if err != nil { if err != nil {
return err return err
} }
extraJars = append(extraJars, jars...) for _, jar := range jars {
if seenJars[jar] {
continue
}
seenJars[jar] = true
extraJars = append(extraJars, jar)
}
switch { switch {
case p.PkgPath == "net": case p.PkgPath == "net":
perms = append(perms, "network") perms = append(perms, "network")
@@ -179,9 +191,9 @@ func buildAndroid(tmpDir string, bi *buildInfo) error {
} }
func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err error) { func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err error) {
androidHome := os.Getenv("ANDROID_SDK_ROOT") androidHome := os.Getenv("ANDROID_HOME")
if androidHome == "" { if androidHome == "" {
return errors.New("ANDROID_SDK_ROOT is not set. Please point it to the root of the Android SDK") return errors.New("ANDROID_HOME is not set. Please point it to the root of the Android SDK")
} }
javac, err := findJavaC() javac, err := findJavaC()
if err != nil { if err != nil {
@@ -191,10 +203,7 @@ func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err erro
if err != nil { if err != nil {
return err return err
} }
minSDK := 17 minSDK := max(bi.minsdk, 17)
if bi.minsdk > minSDK {
minSDK = bi.minsdk
}
tcRoot := filepath.Join(ndkRoot, "toolchains", "llvm", "prebuilt", archNDK()) tcRoot := filepath.Join(ndkRoot, "toolchains", "llvm", "prebuilt", archNDK())
var builds errgroup.Group var builds errgroup.Group
for _, a := range bi.archs { for _, a := range bi.archs {
@@ -213,14 +222,14 @@ func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err erro
} }
} }
archDir := filepath.Join(tmpDir, "jni", arch.jniArch) archDir := filepath.Join(tmpDir, "jni", arch.jniArch)
if err := os.MkdirAll(archDir, 0755); err != nil { if err := os.MkdirAll(archDir, 0o755); err != nil {
return fmt.Errorf("failed to create %q: %v", archDir, err) return fmt.Errorf("failed to create %q: %v", archDir, err)
} }
libFile := filepath.Join(archDir, "libgio.so") libFile := filepath.Join(archDir, "libgio.so")
cmd := exec.Command( cmd := exec.Command(
"go", "go",
"build", "build",
"-ldflags=-w -s "+bi.ldflags, "-ldflags=-w -s -extldflags \"-Wl,-z,max-page-size=65536\" "+bi.ldflags,
"-buildmode=c-shared", "-buildmode=c-shared",
"-tags", bi.tags, "-tags", bi.tags,
"-o", libFile, "-o", libFile,
@@ -233,6 +242,7 @@ func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err erro
"GOARM=7", // Avoid softfloat. "GOARM=7", // Avoid softfloat.
"CGO_ENABLED=1", "CGO_ENABLED=1",
"CC="+clang, "CC="+clang,
"CXX="+clang+"++",
) )
builds.Go(func() error { builds.Go(func() error {
_, err := runCmd(cmd) _, err := runCmd(cmd)
@@ -252,7 +262,7 @@ func compileAndroid(tmpDir string, tools *androidTools, bi *buildInfo) (err erro
} }
if len(javaFiles) > 0 { if len(javaFiles) > 0 {
classes := filepath.Join(tmpDir, "classes") classes := filepath.Join(tmpDir, "classes")
if err := os.MkdirAll(classes, 0755); err != nil { if err := os.MkdirAll(classes, 0o755); err != nil {
return err return err
} }
javac := exec.Command( javac := exec.Command(
@@ -349,15 +359,12 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
}) })
classFiles = append(classFiles, extraJars...) classFiles = append(classFiles, extraJars...)
dexDir := filepath.Join(tmpDir, "apk") dexDir := filepath.Join(tmpDir, "apk")
if err := os.MkdirAll(dexDir, 0755); err != nil { if err := os.MkdirAll(dexDir, 0o755); err != nil {
return err return err
} }
minSDK := 16 minSDK := max(bi.minsdk, 16)
if bi.minsdk > minSDK {
minSDK = bi.minsdk
}
// https://developer.android.com/distribute/best-practices/develop/target-sdk // https://developer.android.com/distribute/best-practices/develop/target-sdk
targetSDK := 33 targetSDK := 35
if bi.targetsdk > 0 { if bi.targetsdk > 0 {
targetSDK = bi.targetsdk targetSDK = bi.targetsdk
} }
@@ -387,7 +394,7 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
v21Dir := filepath.Join(resDir, "values-v21") v21Dir := filepath.Join(resDir, "values-v21")
v26mipmapDir := filepath.Join(resDir, `mipmap-anydpi-v26`) v26mipmapDir := filepath.Join(resDir, `mipmap-anydpi-v26`)
for _, dir := range []string{valDir, v21Dir, v26mipmapDir} { for _, dir := range []string{valDir, v21Dir, v26mipmapDir} {
if err := os.MkdirAll(dir, 0755); err != nil { if err := os.MkdirAll(dir, 0o755); err != nil {
return err return err
} }
} }
@@ -411,20 +418,24 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_adaptive" /> <background android:drawable="@mipmap/ic_launcher_adaptive" />
<foreground android:drawable="@mipmap/ic_launcher_adaptive" /> <foreground android:drawable="@mipmap/ic_launcher_adaptive" />
</adaptive-icon>`), 0660) </adaptive-icon>`), 0o660)
if err != nil { if err != nil {
return err return err
} }
iconSnip = `android:icon="@mipmap/ic_launcher"` iconSnip = `android:icon="@mipmap/ic_launcher"`
} }
err = os.WriteFile(filepath.Join(valDir, "themes.xml"), []byte(themes), 0660) err = os.WriteFile(filepath.Join(valDir, "themes.xml"), []byte(themes), 0o660)
if err != nil { if err != nil {
return err return err
} }
err = os.WriteFile(filepath.Join(v21Dir, "themes.xml"), []byte(themesV21), 0660) err = os.WriteFile(filepath.Join(v21Dir, "themes.xml"), []byte(themesV21), 0o660)
if err != nil { if err != nil {
return err return err
} }
moduleRoot := moduleRootForDir(bi.pkgDir)
if err := copyTree(filepath.Join(moduleRoot, "android", "res"), resDir); err != nil {
return err
}
resZip := filepath.Join(tmpDir, "resources.zip") resZip := filepath.Join(tmpDir, "resources.zip")
aapt2 := filepath.Join(tools.buildtools, "aapt2") aapt2 := filepath.Join(tools.buildtools, "aapt2")
_, err = runCmd(exec.Command( _, err = runCmd(exec.Command(
@@ -440,44 +451,25 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe
permissions, features := getPermissions(perms) permissions, features := getPermissions(perms)
appName := UppercaseName(bi.name) appName := UppercaseName(bi.name)
manifestSrc := manifestData{ manifestSrc := manifestData{
AppID: bi.appID, AppID: bi.appID,
Version: bi.version, Version: bi.version,
MinSDK: minSDK, MinSDK: minSDK,
TargetSDK: targetSDK, TargetSDK: targetSDK,
Permissions: permissions, Permissions: permissions,
Features: features, Features: features,
IconSnip: iconSnip, IconSnip: iconSnip,
AppName: appName, AppName: appName,
Schemes: bi.schemes,
PackageQueries: bi.packageQueries,
ManifestSnip: readOptionalText(filepath.Join(moduleRoot, "android", "manifest_snippets.xml")),
AppSnip: readOptionalText(filepath.Join(moduleRoot, "android", "application_snippets.xml")),
} }
tmpl, err := template.New("test").Parse( manifestBuffer, err := renderAndroidManifest(manifestSrc)
`<?xml version="1.0" encoding="utf-8"?> if err != nil {
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="{{.AppID}}"
android:versionCode="{{.Version.VersionCode}}"
android:versionName="{{.Version}}">
<uses-sdk android:minSdkVersion="{{.MinSDK}}" android:targetSdkVersion="{{.TargetSDK}}" />
{{range .Permissions}} <uses-permission android:name="{{.}}"/>
{{end}}{{range .Features}} <uses-feature android:{{.}} android:required="false"/>
{{end}} <application {{.IconSnip}} android:label="{{.AppName}}">
<activity android:name="org.gioui.GioActivity"
android:label="{{.AppName}}"
android:theme="@style/Theme.GioApp"
android:configChanges="screenSize|screenLayout|smallestScreenSize|orientation|keyboardHidden"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>`)
var manifestBuffer bytes.Buffer
if err := tmpl.Execute(&manifestBuffer, manifestSrc); err != nil {
return err return err
} }
manifest := filepath.Join(tmpDir, "AndroidManifest.xml") manifest := filepath.Join(tmpDir, "AndroidManifest.xml")
if err := os.WriteFile(manifest, manifestBuffer.Bytes(), 0660); err != nil { if err := os.WriteFile(manifest, manifestBuffer, 0o660); err != nil {
return err return err
} }
@@ -679,7 +671,7 @@ func signAAB(tmpDir string, aabFile string, tools *androidTools, bi *buildInfo)
} }
var alias string var alias string
for _, t := range strings.Split(keytoolList, "\n") { for t := range strings.SplitSeq(keytoolList, "\n") {
if i, _ := fmt.Sscanf(t, "Alias name: %s", &alias); i > 0 { if i, _ := fmt.Sscanf(t, "Alias name: %s", &alias); i > 0 {
break break
} }
@@ -742,6 +734,141 @@ func defaultAndroidKeystore(tmpDir string, bi *buildInfo) error {
return err return err
} }
func renderAndroidManifest(data manifestData) ([]byte, error) {
tmpl, err := template.New("test").Parse(
`<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="{{.AppID}}"
android:versionCode="{{.Version.VersionCode}}"
android:versionName="{{.Version}}">
{{if .PackageQueries}}
<queries>
{{range .PackageQueries}}
<package android:name="{{.}}" />
{{end}}
</queries>
{{end}}
<uses-sdk android:minSdkVersion="{{.MinSDK}}" android:targetSdkVersion="{{.TargetSDK}}" />
{{range .Permissions}} <uses-permission android:name="{{.}}"/>
{{end}}{{range .Features}} <uses-feature android:{{.}} android:required="false"/>
{{end}}{{.ManifestSnip}} <application {{.IconSnip}} android:label="{{.AppName}}">
{{.AppSnip}}
<activity android:name="org.gioui.GioActivity"
android:label="{{.AppName}}"
android:theme="@style/Theme.GioApp"
android:configChanges="screenSize|screenLayout|smallestScreenSize|orientation|keyboardHidden"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleInstance"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
{{range .Schemes}}
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:scheme="{{.}}"></data>
</intent-filter>
{{end}}
</activity>
</application>
</manifest>`)
if err != nil {
return nil, err
}
var manifestBuffer bytes.Buffer
if err := tmpl.Execute(&manifestBuffer, data); err != nil {
return nil, err
}
return manifestBuffer.Bytes(), nil
}
func readOptionalText(path string) string {
data, err := os.ReadFile(path)
if err != nil {
return ""
}
if len(data) == 0 {
return ""
}
return "\n" + string(data) + "\n"
}
func copyTree(src, dst string) error {
info, err := os.Stat(src)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
if !info.IsDir() {
return fmt.Errorf("extra Android resources path is not a directory: %s", src)
}
return filepath.Walk(src, func(path string, entry os.FileInfo, walkErr error) error {
if walkErr != nil {
return walkErr
}
rel, err := filepath.Rel(src, path)
if err != nil {
return err
}
if rel == "." {
return nil
}
target := filepath.Join(dst, rel)
if entry.IsDir() {
return os.MkdirAll(target, 0o755)
}
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
return err
}
data, err := os.ReadFile(path)
if err != nil {
return err
}
return os.WriteFile(target, data, 0o660)
})
}
func androidExtraJars(dir, moduleRoot string) ([]string, error) {
var jars []string
patterns := []string{
filepath.Join(dir, "*.jar"),
filepath.Join(dir, "android", "*.jar"),
}
if moduleRoot != "" && moduleRoot != dir {
patterns = append(patterns,
filepath.Join(moduleRoot, "*.jar"),
filepath.Join(moduleRoot, "android", "*.jar"),
)
}
for _, pattern := range patterns {
matches, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
jars = append(jars, matches...)
}
return jars, nil
}
func moduleRootForDir(dir string) string {
current := dir
for {
if _, err := os.Stat(filepath.Join(current, "go.mod")); err == nil {
return current
}
parent := filepath.Dir(current)
if parent == current {
return dir
}
current = parent
}
}
func findNDK(androidHome string) (string, error) { func findNDK(androidHome string) (string, error) {
ndks, err := filepath.Glob(filepath.Join(androidHome, "ndk", "*")) ndks, err := filepath.Glob(filepath.Join(androidHome, "ndk", "*"))
if err != nil { if err != nil {
@@ -750,7 +877,7 @@ func findNDK(androidHome string) (string, error) {
if bestNDK, found := latestVersionPath(ndks); found { if bestNDK, found := latestVersionPath(ndks); found {
return bestNDK, nil return bestNDK, nil
} }
// The old NDK path was $ANDROID_SDK_ROOT/ndk-bundle. // The old NDK path was $ANDROID_HOME/ndk-bundle.
ndkBundle := filepath.Join(androidHome, "ndk-bundle") ndkBundle := filepath.Join(androidHome, "ndk-bundle")
if _, err := os.Stat(ndkBundle); err == nil { if _, err := os.Stat(ndkBundle); err == nil {
return ndkBundle, nil return ndkBundle, nil
@@ -763,7 +890,7 @@ func findNDK(androidHome string) (string, error) {
} }
} }
return "", fmt.Errorf("no NDK found in $ANDROID_SDK_ROOT (%s). Set $ANDROID_NDK_ROOT or use `sdkmanager ndk-bundle` to install the NDK", androidHome) return "", fmt.Errorf("no NDK found in $ANDROID_HOME (%s). Set $ANDROID_NDK_ROOT or use `sdkmanager ndk-bundle` to install the NDK", androidHome)
} }
func findKeytool() (string, error) { func findKeytool() (string, error) {
+193
View File
@@ -0,0 +1,193 @@
// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestAndroidExtraJarsIncludesAndroidSubdirectory(t *testing.T) {
t.Parallel()
dir := t.TempDir()
rootJar := filepath.Join(dir, "crew.jar")
androidJar := filepath.Join(dir, "android", "vault.jar")
writeTestFile(t, rootJar, "root")
writeTestFile(t, androidJar, "android")
got, err := androidExtraJars(dir, dir)
if err != nil {
t.Fatalf("androidExtraJars() error = %v", err)
}
want := []string{rootJar, androidJar}
for _, jar := range want {
if !containsString(got, jar) {
t.Fatalf("androidExtraJars() = %v, want %q included", got, jar)
}
}
}
func TestAndroidExtraJarsIncludesModuleRootAndroidSubdirectory(t *testing.T) {
t.Parallel()
root := t.TempDir()
dir := filepath.Join(root, "cmd", "keepassgo")
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", dir, err)
}
moduleAndroidJar := filepath.Join(root, "android", "keepassgo-android.jar")
writeTestFile(t, moduleAndroidJar, "module-android")
got, err := androidExtraJars(dir, root)
if err != nil {
t.Fatalf("androidExtraJars() error = %v", err)
}
if !containsString(got, moduleAndroidJar) {
t.Fatalf("androidExtraJars() = %v, want %q included", got, moduleAndroidJar)
}
}
func TestAndroidExtraJarsDoesNotRepeatSharedModuleJarWhenCollectedAcrossPackages(t *testing.T) {
t.Parallel()
root := t.TempDir()
moduleAndroidJar := filepath.Join(root, "android", "keepassgo-android.jar")
writeTestFile(t, filepath.Join(root, "go.mod"), "module example.invalid/crew\n")
writeTestFile(t, moduleAndroidJar, "module-android")
dirs := []string{
filepath.Join(root, "cmd", "keepassgo"),
filepath.Join(root, "internal", "appui"),
}
seen := make(map[string]bool)
var collected []string
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", dir, err)
}
jars, err := androidExtraJars(dir, root)
if err != nil {
t.Fatalf("androidExtraJars(%q) error = %v", dir, err)
}
for _, jar := range jars {
if seen[jar] {
continue
}
seen[jar] = true
collected = append(collected, jar)
}
}
count := 0
for _, jar := range collected {
if jar == moduleAndroidJar {
count++
}
}
if count != 1 {
t.Fatalf("collected module jar count = %d, want 1 in %v", count, collected)
}
}
func TestRenderAndroidManifestIncludesOptionalSnippets(t *testing.T) {
t.Parallel()
manifest, err := renderAndroidManifest(manifestData{
AppID: "org.example.heist",
Version: Semver{Major: 1, Minor: 2, Patch: 3, VersionCode: 4},
MinSDK: 28,
TargetSDK: 35,
Permissions: []string{"android.permission.INTERNET"},
Features: []string{`name="android.hardware.fingerprint"`},
IconSnip: `android:icon="@mipmap/ic_launcher"`,
AppName: "Bellagio Crew",
ManifestSnip: "\n\t<uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n",
AppSnip: "\n\t\t<service android:name=\".CrewService\" />\n",
})
if err != nil {
t.Fatalf("renderAndroidManifest() error = %v", err)
}
got := string(manifest)
for _, want := range []string{
`android.permission.POST_NOTIFICATIONS`,
`<service android:name=".CrewService" />`,
`android.permission.INTERNET`,
`android.hardware.fingerprint`,
} {
if !strings.Contains(got, want) {
t.Fatalf("renderAndroidManifest() missing %q in %s", want, got)
}
}
}
func TestCopyTreeCopiesNestedResources(t *testing.T) {
t.Parallel()
src := filepath.Join(t.TempDir(), "android", "res")
dst := filepath.Join(t.TempDir(), "merged")
resourcePath := filepath.Join(src, "xml", "heist_service.xml")
writeTestFile(t, resourcePath, "<service />")
if err := copyTree(src, dst); err != nil {
t.Fatalf("copyTree() error = %v", err)
}
got, err := os.ReadFile(filepath.Join(dst, "xml", "heist_service.xml"))
if err != nil {
t.Fatalf("ReadFile() error = %v", err)
}
if string(got) != "<service />" {
t.Fatalf("copyTree() copied %q, want %q", string(got), "<service />")
}
}
func TestModuleRootForDirFindsOwningModule(t *testing.T) {
t.Parallel()
root := t.TempDir()
writeTestFile(t, filepath.Join(root, "go.mod"), "module example.invalid/crew\n")
dir := filepath.Join(root, "cmd", "keepassgo")
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", dir, err)
}
if got := moduleRootForDir(dir); got != root {
t.Fatalf("moduleRootForDir(%q) = %q, want %q", dir, got, root)
}
}
func TestModuleRootForDirFallsBackToInputDir(t *testing.T) {
t.Parallel()
dir := filepath.Join(t.TempDir(), "cmd", "keepassgo")
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", dir, err)
}
if got := moduleRootForDir(dir); got != dir {
t.Fatalf("moduleRootForDir(%q) = %q, want %q", dir, got, dir)
}
}
func writeTestFile(t *testing.T, path, contents string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", path, err)
}
if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
func containsString(values []string, want string) bool {
for _, value := range values {
if value == want {
return true
}
}
return false
}
+26 -7
View File
@@ -31,6 +31,8 @@ type buildInfo struct {
notaryAppleID string notaryAppleID string
notaryPassword string notaryPassword string
notaryTeamID string notaryTeamID string
schemes []string
packageQueries []string
} }
type Semver struct { type Semver struct {
@@ -56,6 +58,10 @@ func newBuildInfo(pkgPath string) (*buildInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sp := *signPass
if sp == "" {
sp = os.Getenv("GOGIO_SIGNPASS")
}
bi := &buildInfo{ bi := &buildInfo{
appID: appID, appID: appID,
archs: getArchs(), archs: getArchs(),
@@ -70,10 +76,12 @@ func newBuildInfo(pkgPath string) (*buildInfo, error) {
target: *target, target: *target,
version: ver, version: ver,
key: *signKey, key: *signKey,
password: *signPass, password: sp,
notaryAppleID: *notaryID, notaryAppleID: *notaryID,
notaryPassword: *notaryPass, notaryPassword: *notaryPass,
notaryTeamID: *notaryTeamID, notaryTeamID: *notaryTeamID,
schemes: getCommaList(*schemes),
packageQueries: getCommaList(*pkgQueries),
} }
return bi, nil return bi, nil
} }
@@ -88,14 +96,16 @@ func (s Semver) String() string {
return fmt.Sprintf("%d.%d.%d.%d", s.Major, s.Minor, s.Patch, s.VersionCode) return fmt.Sprintf("%d.%d.%d.%d", s.Major, s.Minor, s.Patch, s.VersionCode)
} }
func (s Semver) StringCompact() string {
// Used to meet CFBundleShortVersionString format.
return fmt.Sprintf("%d.%d.%d", s.Major, s.Minor, s.Patch)
}
func parseSemver(v string) (Semver, error) { func parseSemver(v string) (Semver, error) {
var sv Semver var sv Semver
_, err := fmt.Sscanf(v, "%d.%d.%d.%d", &sv.Major, &sv.Minor, &sv.Patch, &sv.VersionCode) _, err := fmt.Sscanf(v, "%d.%d.%d.%d", &sv.Major, &sv.Minor, &sv.Patch, &sv.VersionCode)
if err != nil { if err != nil || sv.String() != v {
return Semver{}, fmt.Errorf("invalid semver: %q", v) return Semver{}, fmt.Errorf("invalid semver: %q (must match major.minor.patch.versioncode)", v)
}
if sv.String() != v {
return Semver{}, fmt.Errorf("invalid semver: %q", v)
} }
return sv, nil return sv, nil
} }
@@ -146,6 +156,15 @@ func getLdFlags(appID string) string {
return strings.Join(ldflags, " ") return strings.Join(ldflags, " ")
} }
func getCommaList(s string) (list []string) {
for _, v := range strings.Split(s, ",") {
if v := strings.TrimSpace(v); v != "" {
list = append(list, v)
}
}
return list
}
type packageMetadata struct { type packageMetadata struct {
PkgPath string PkgPath string
Dir string Dir string
@@ -180,7 +199,7 @@ func getAppID(pkgMetadata *packageMetadata) string {
name = "." + domain[0] name = "." + domain[0]
domain[0] = "localhost" domain[0] = "localhost"
} else { } else {
for i := 0; i < len(domain)/2; i++ { for i := range len(domain) / 2 {
opp := len(domain) - 1 - i opp := len(domain) - 1 - i
domain[i], domain[opp] = domain[opp], domain[i] domain[i], domain[opp] = domain[opp], domain[i]
} }
+32 -5
View File
@@ -2,14 +2,12 @@ package main
import "testing" import "testing"
type expval struct {
in, out string
}
func TestAppID(t *testing.T) { func TestAppID(t *testing.T) {
t.Parallel() t.Parallel()
tests := []expval{ tests := []struct {
in, out string
}{
{"example", "localhost.example"}, {"example", "localhost.example"},
{"example.com", "com.example"}, {"example.com", "com.example"},
{"www.example.com", "com.example.www"}, {"www.example.com", "com.example.www"},
@@ -30,3 +28,32 @@ func TestAppID(t *testing.T) {
} }
} }
} }
func TestVersion(t *testing.T) {
t.Parallel()
tests := []struct {
version string
valid bool
}{
{"v1", false},
{"v10.21.333.12", false},
{"1.2.3", false},
{"1.2.3.4", true},
}
for i, test := range tests {
ver, err := parseSemver(test.version)
if err != nil {
if test.valid {
t.Errorf("(%d): %q failed to parse: %v", i, test.version, err)
}
continue
} else if !test.valid {
t.Errorf("(%d): %q was unexpectedly accepted", i, test.version)
}
if got := ver.String(); got != test.version {
t.Errorf("(%d): %q parsed to %q", i, test.version, got)
}
}
}
+15 -3
View File
@@ -47,8 +47,9 @@ The -appid flag specifies the package name for Android or the bundle id for
iOS and tvOS. A bundle id must be provisioned through Xcode before the gogio iOS and tvOS. A bundle id must be provisioned through Xcode before the gogio
tool can use it. tool can use it.
The -version flag specifies the integer version code for Android and the last The -version flag specifies the semantic version for -buildmode exe. It must
component of the 1.0.X version for iOS and tvOS. be on the form major.minor.patch.versioncode where the version code is used for
the integer version number for Android, iOS and tvOS.
For Android builds the -minsdk flag specify the minimum SDK level. For example, For Android builds the -minsdk flag specify the minimum SDK level. For example,
use -minsdk 22 to target Android 5.1 (Lollipop) and later. use -minsdk 22 to target Android 5.1 (Lollipop) and later.
@@ -68,9 +69,11 @@ its deletion.
The -x flag will print all the external commands executed by the gogio tool. The -x flag will print all the external commands executed by the gogio tool.
The -signkey flag specifies the path of the keystore, used for signing Android apk/aab files The -signkey flag specifies the path of the keystore, used for signing Android apk/aab files
or specifies the name of key on Keychain to sign MacOS app. or specifies the name of key on Keychain to sign MacOS apps. On iOS and macOS it can be used
to specify the path of a provisioning profile (.mobileprovision/.provisionprofile).
The -signpass flag specifies the password of the keystore, ignored if -signkey is not provided. The -signpass flag specifies the password of the keystore, ignored if -signkey is not provided.
If -signpass is not sepecified it will be read from the environment variable GOGIO_SIGNPASS.
The -notaryid flag specifies the Apple ID to use for notarization of MacOS app. The -notaryid flag specifies the Apple ID to use for notarization of MacOS app.
@@ -80,4 +83,13 @@ for details. If not provided, the password will be prompted.
The -notaryteamid flag specifies the team ID to use for notarization of MacOS app, ignored if The -notaryteamid flag specifies the team ID to use for notarization of MacOS app, ignored if
-notaryid is not provided. -notaryid is not provided.
The -schemes flag specifies a list of comma separated URI schemes that the program can
handle. For example, use -schemes yourAppName to receive a app.URLEvent for URIs
starting with yourAppName://. It is only supported on Android, iOS, macOS and Windows.
On Windows, it will restrict the program to a single instance.
The -queries flag specifies a list of comma separated package names used to query other apps,
that is useful to launch other apps and verify their presence. For example, use -queries
com.example.otherapp to query the app with that package name. It is only necessary on Android.
` `
+19 -15
View File
@@ -20,8 +20,8 @@ import (
"gioui.org/app" "gioui.org/app"
"gioui.org/gpu" "gioui.org/gpu"
"gioui.org/io/event"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/system"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
@@ -98,7 +98,8 @@ type eglContext struct {
func main() { func main() {
go func() { go func() {
// Set CustomRenderer so we can provide our own rendering context. // Set CustomRenderer so we can provide our own rendering context.
w := app.NewWindow(app.CustomRenderer(true)) w := new(app.Window)
w.Option(app.CustomRenderer(true))
if err := loop(w); err != nil { if err := loop(w); err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -164,17 +165,17 @@ func loop(w *app.Window) error {
// eglMakeCurrent binds a context to an operating system thread. Prevent Go from switching thread. // eglMakeCurrent binds a context to an operating system thread. Prevent Go from switching thread.
runtime.LockOSThread() runtime.LockOSThread()
for e := range w.Events() { for {
switch e := e.(type) { switch e := w.Event().(type) {
case app.ViewEvent: case app.ViewEvent:
ve = e ve = e
init = true init = true
if size != (image.Point{}) { if size != (image.Point{}) {
recreateContext() recreateContext()
} }
case system.DestroyEvent: case app.DestroyEvent:
return e.Err return e.Err
case system.FrameEvent: case app.FrameEvent:
if init && size != e.Size { if init && size != e.Size {
size = e.Size size = e.Size
recreateContext() recreateContext()
@@ -183,7 +184,7 @@ func loop(w *app.Window) error {
break break
} }
// Build ops. // Build ops.
gtx := layout.NewContext(&ops, e) gtx := app.NewContext(&ops, e)
// Clear background to white, even on embedded platforms such as webassembly. // Clear background to white, even on embedded platforms such as webassembly.
paint.Fill(gtx.Ops, color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}) paint.Fill(gtx.Ops, color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff})
@@ -205,7 +206,7 @@ func loop(w *app.Window) error {
) )
}), }),
) )
op.InvalidateOp{}.Add(gtx.Ops) gtx.Execute(op.InvalidateCmd{})
log.Println("frame") log.Println("frame")
// Trigger window resize detection in ANGLE. // Trigger window resize detection in ANGLE.
@@ -351,13 +352,16 @@ func (w *quarterWidget) Layout(gtx layout.Context) layout.Dimensions {
defer clip.Rect(image.Rectangle{ defer clip.Rect(image.Rectangle{
Max: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y), Max: image.Pt(gtx.Constraints.Max.X, gtx.Constraints.Max.Y),
}).Push(gtx.Ops).Pop() }).Push(gtx.Ops).Pop()
pointer.InputOp{ event.Op(gtx.Ops, w)
Tag: w, for {
Types: pointer.Press, e, ok := gtx.Event(pointer.Filter{
}.Add(gtx.Ops) Target: w,
Kinds: pointer.Press,
for _, e := range gtx.Events(w) { })
if e, ok := e.(pointer.Event); ok && e.Type == pointer.Press { if !ok {
break
}
if e, ok := e.(pointer.Event); ok && e.Kind == pointer.Press {
w.clicked = !w.clicked w.clicked = !w.clicked
// notify when we're done updating the frame. // notify when we're done updating the frame.
notify = notifyInvalidate notify = notifyInvalidate
+160 -57
View File
@@ -4,6 +4,7 @@ package main
import ( import (
"archive/zip" "archive/zip"
"bytes"
"crypto/sha1" "crypto/sha1"
"encoding/hex" "encoding/hex"
"errors" "errors"
@@ -12,8 +13,10 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"slices"
"strconv" "strconv"
"strings" "strings"
"text/template"
"time" "time"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@@ -56,7 +59,7 @@ func buildIOS(tmpDir, target string, bi *buildInfo) error {
} }
} }
bi.archs = append(bi.archs[:i], bi.archs[i+1:]...) bi.archs = slices.Delete(bi.archs, i, i+1)
} }
if !forDevice && !strings.HasSuffix(out, ".app") { if !forDevice && !strings.HasSuffix(out, ".app") {
return fmt.Errorf("the specified output directory %q does not end in .app or .ipa", out) return fmt.Errorf("the specified output directory %q does not end in .app or .ipa", out)
@@ -66,13 +69,35 @@ func buildIOS(tmpDir, target string, bi *buildInfo) error {
} }
payload := filepath.Join(tmpDir, "Payload") payload := filepath.Join(tmpDir, "Payload")
appDir := filepath.Join(payload, appName+".app") appDir := filepath.Join(payload, appName+".app")
if err := os.MkdirAll(appDir, 0755); err != nil { if err := os.MkdirAll(appDir, 0o755); err != nil {
return err return err
} }
if err := exeIOS(tmpDir, target, appDir, bi); err != nil { if err := exeIOS(tmpDir, target, appDir, bi); err != nil {
return err return err
} }
if err := signIOS(bi, tmpDir, appDir); err != nil {
embedded := filepath.Join(appDir, "embedded.mobileprovision")
var provisions []string
if bi.key != "" {
if ext := filepath.Ext(bi.key); ext != ".mobileprovision" && ext != ".provisionprofile" {
return fmt.Errorf("sign: -signkey specifies an Apple provisioning profile, but %q does not end in .mobileprovision or .provisionprofile", bi.key)
}
provisions = []string{bi.key}
} else {
home, err := os.UserHomeDir()
if err != nil {
return err
}
p, err := filepath.Glob(filepath.Join(home, "Library", "MobileDevice", "Provisioning Profiles", "*.mobileprovision"))
if err != nil {
return err
}
provisions = p
}
if err := signApple(bi.appID, tmpDir, embedded, appDir, provisions); err != nil {
return err return err
} }
return zipDir(out, tmpDir, "Payload") return zipDir(out, tmpDir, "Payload")
@@ -81,16 +106,8 @@ func buildIOS(tmpDir, target string, bi *buildInfo) error {
} }
} }
func signIOS(bi *buildInfo, tmpDir, app string) error { // signApple is shared between iOS and macOS.
home, err := os.UserHomeDir() func signApple(appID, tmpDir, embedded, app string, provisions []string) error {
if err != nil {
return err
}
provPattern := filepath.Join(home, "Library", "MobileDevice", "Provisioning Profiles", "*.mobileprovision")
provisions, err := filepath.Glob(provPattern)
if err != nil {
return err
}
provInfo := filepath.Join(tmpDir, "provision.plist") provInfo := filepath.Join(tmpDir, "provision.plist")
var avail []string var avail []string
for _, prov := range provisions { for _, prov := range provisions {
@@ -114,17 +131,23 @@ func signIOS(bi *buildInfo, tmpDir, app string) error {
if err != nil { if err != nil {
return err return err
} }
provAppID, err := runCmd(exec.Command("/usr/libexec/PlistBuddy", "-c", "Print:Entitlements:application-identifier", provInfo))
// iOS/macOS Catalyst
provAppIDSearchKey := "Print:Entitlements:application-identifier"
if filepath.Ext(prov) == ".provisionprofile" {
// macOS
provAppIDSearchKey = "Print:Entitlements:com.apple.application-identifier"
}
provAppID, err := runCmd(exec.Command("/usr/libexec/PlistBuddy", "-c", provAppIDSearchKey, provInfo))
if err != nil { if err != nil {
return err return err
} }
expAppID := fmt.Sprintf("%s.%s", appIDPrefix, bi.appID) expAppID := fmt.Sprintf("%s.%s", appIDPrefix, appID)
avail = append(avail, provAppID) avail = append(avail, provAppID)
if expAppID != provAppID { if expAppID != provAppID {
continue continue
} }
// Copy provisioning file. // Copy provisioning file.
embedded := filepath.Join(app, "embedded.mobileprovision")
if err := copyFile(embedded, prov); err != nil { if err := copyFile(embedded, prov); err != nil {
return err return err
} }
@@ -139,15 +162,23 @@ func signIOS(bi *buildInfo, tmpDir, app string) error {
return err return err
} }
entFile := filepath.Join(tmpDir, "entitlements.plist") entFile := filepath.Join(tmpDir, "entitlements.plist")
if err := os.WriteFile(entFile, []byte(entitlements), 0660); err != nil { if err := os.WriteFile(entFile, []byte(entitlements), 0o660); err != nil {
return err return err
} }
identity := sha1.Sum(certDER) identity := sha1.Sum(certDER)
idHex := hex.EncodeToString(identity[:]) idHex := hex.EncodeToString(identity[:])
_, err = runCmd(exec.Command("codesign", "-s", idHex, "-v", "--entitlements", entFile, app)) _, err = runCmd(exec.Command(
"codesign",
"--sign", idHex,
"--deep",
"--force",
"--options", "runtime",
"--entitlements",
entFile,
app))
return err return err
} }
return fmt.Errorf("sign: no valid provisioning profile found for bundle id %q among %v", bi.appID, avail) return fmt.Errorf("sign: no valid provisioning profile found for bundle id %q among %v", appID, avail)
} }
func exeIOS(tmpDir, target, app string, bi *buildInfo) error { func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
@@ -157,7 +188,7 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
if err := os.RemoveAll(app); err != nil { if err := os.RemoveAll(app); err != nil {
return err return err
} }
if err := os.Mkdir(app, 0755); err != nil { if err := os.Mkdir(app, 0o755); err != nil {
return err return err
} }
appName := UppercaseName(bi.name) appName := UppercaseName(bi.name)
@@ -171,6 +202,7 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
} }
cflags = append(cflags, cflags = append(cflags,
"-fobjc-arc", "-fobjc-arc",
fmt.Sprintf("-miphoneos-version-min=%d.0", bi.minsdk),
) )
cflagsLine := strings.Join(cflags, " ") cflagsLine := strings.Join(cflags, " ")
exeSlice := filepath.Join(tmpDir, "app-"+a) exeSlice := filepath.Join(tmpDir, "app-"+a)
@@ -189,7 +221,9 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
"GOARCH="+a, "GOARCH="+a,
"CGO_ENABLED=1", "CGO_ENABLED=1",
"CC="+clang, "CC="+clang,
"CXX="+clang+"++",
"CGO_CFLAGS="+cflagsLine, "CGO_CFLAGS="+cflagsLine,
"CGO_CXXFLAGS="+cflagsLine,
"CGO_LDFLAGS=-lresolv "+cflagsLine, "CGO_LDFLAGS=-lresolv "+cflagsLine,
) )
builds.Go(func() error { builds.Go(func() error {
@@ -205,7 +239,7 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
} }
infoPlist := buildInfoPlist(bi) infoPlist := buildInfoPlist(bi)
plistFile := filepath.Join(app, "Info.plist") plistFile := filepath.Join(app, "Info.plist")
if err := os.WriteFile(plistFile, []byte(infoPlist), 0660); err != nil { if err := os.WriteFile(plistFile, []byte(infoPlist), 0o660); err != nil {
return err return err
} }
if _, err := os.Stat(bi.iconPath); err == nil { if _, err := os.Stat(bi.iconPath); err == nil {
@@ -233,13 +267,16 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error {
// iosIcons returns the asset plist file to be merged into Info.plist. // iosIcons returns the asset plist file to be merged into Info.plist.
func iosIcons(bi *buildInfo, tmpDir, appDir, icon string) (string, error) { func iosIcons(bi *buildInfo, tmpDir, appDir, icon string) (string, error) {
assets := filepath.Join(tmpDir, "Assets.xcassets") assets := filepath.Join(tmpDir, "Assets.xcassets")
if err := os.Mkdir(assets, 0700); err != nil { if err := os.Mkdir(assets, 0o700); err != nil {
return "", err return "", err
} }
appIcon := filepath.Join(assets, "AppIcon.appiconset") appIcon := filepath.Join(assets, "AppIcon.appiconset")
err := buildIcons(appIcon, icon, []iconVariant{ err := buildIcons(appIcon, icon, []iconVariant{
{path: "ios_2x.png", size: 120}, {path: "ios_2x.png", size: 120},
{path: "ios_3x.png", size: 180}, {path: "ios_3x.png", size: 180},
{path: "ipad_1x.png", size: 76},
{path: "ipad_2x.png", size: 152},
{path: "ipad_4x.png", size: 228},
// The App Store icon is not allowed to contain // The App Store icon is not allowed to contain
// transparent pixels. // transparent pixels.
{path: "ios_store.png", size: 1024, fill: true}, {path: "ios_store.png", size: 1024, fill: true},
@@ -248,29 +285,47 @@ func iosIcons(bi *buildInfo, tmpDir, appDir, icon string) (string, error) {
return "", err return "", err
} }
contentJson := `{ contentJson := `{
"images" : [ "images": [
{ {
"size" : "60x60", "size": "60x60",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "ios_2x.png", "filename": "ios_2x.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "60x60", "size": "60x60",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "ios_3x.png", "filename": "ios_3x.png",
"scale" : "3x" "scale": "3x"
}, },
{ {
"size" : "1024x1024", "size": "76x76",
"idiom" : "ios-marketing", "idiom": "ipad",
"filename" : "ios_store.png", "filename": "ipad_1x.png",
"scale" : "1x" "scale": "1x"
} },
] {
"size": "76x76",
"idiom": "ipad",
"filename": "ipad_2x.png",
"scale": "2x"
},
{
"size": "152x152",
"idiom": "ipad",
"filename": "ipad_4x.png",
"scale": "2x"
},
{
"size": "1024x1024",
"idiom": "ios-marketing",
"filename": "ios_store.png",
"scale": "1x"
}
]
}` }`
contentFile := filepath.Join(appIcon, "Contents.json") contentFile := filepath.Join(appIcon, "Contents.json")
if err := os.WriteFile(contentFile, []byte(contentJson), 0600); err != nil { if err := os.WriteFile(contentFile, []byte(contentJson), 0o600); err != nil {
return "", err return "", err
} }
assetPlist := filepath.Join(tmpDir, "assets.plist") assetPlist := filepath.Join(tmpDir, "assets.plist")
@@ -301,36 +356,57 @@ func buildInfoPlist(bi *buildInfo) string {
case "tvos": case "tvos":
supportPlatform = "AppleTVOS" supportPlatform = "AppleTVOS"
} }
return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
manifestSrc := struct {
AppName string
AppID string
Version string
VersionCode uint32
Platform string
MinVersion int
SupportPlatform string
Schemes []string
}{
AppName: appName,
AppID: bi.appID,
Version: bi.version.StringCompact(),
VersionCode: bi.version.VersionCode,
Platform: platform,
MinVersion: bi.minsdk,
SupportPlatform: supportPlatform,
Schemes: bi.schemes,
}
tmpl, err := template.New("manifest").Parse(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>%s</string> <string>{{.AppName}}</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>%s</string> <string>{{.AppID}}</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>%s</string> <string>{{.AppName}}</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>%s</string> <string>{{.Version}}</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>%d</string> <string>{{.VersionCode}}</string>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key> <key>UIRequiredDeviceCapabilities</key>
<array><string>arm64</string></array> <array><string>arm64</string></array>
<key>DTPlatformName</key> <key>DTPlatformName</key>
<string>%s</string> <string>{{.Platform}}</string>
<key>DTPlatformVersion</key> <key>DTPlatformVersion</key>
<string>12.4</string> <string>12.4</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>%d</string> <string>{{.MinVersion}}.0</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>
@@ -338,14 +414,17 @@ func buildInfoPlist(bi *buildInfo) string {
</array> </array>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>%s</string> <string>{{.SupportPlatform}}</string>
</array> </array>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIRequiresFullScreen</key>
<true/>
<key>DTCompiler</key> <key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string> <string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key> <key>DTPlatformBuild</key>
@@ -353,13 +432,38 @@ func buildInfoPlist(bi *buildInfo) string {
<key>DTSDKBuild</key> <key>DTSDKBuild</key>
<string>16G73</string> <string>16G73</string>
<key>DTSDKName</key> <key>DTSDKName</key>
<string>%s12.4</string> <string>{{.Platform}}12.4</string>
<key>DTXcode</key> <key>DTXcode</key>
<string>1030</string> <string>1030</string>
<key>DTXcodeBuild</key> <key>DTXcodeBuild</key>
<string>10G8</string> <string>10G8</string>
<key>UILaunchScreen</key>
<true/>
{{if .Schemes}}
<key>CFBundleURLTypes</key>
<array>
{{range .Schemes}}
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>{{.}}</string>
</array>
</dict>
{{end}}
</array>
{{end}}
</dict> </dict>
</plist>`, appName, bi.appID, appName, bi.version, bi.version.VersionCode, platform, minIOSVersion, supportPlatform, platform) </plist>`)
if err != nil {
panic(err)
}
var manifestBuffer bytes.Buffer
if err := tmpl.Execute(&manifestBuffer, manifestSrc); err != nil {
panic(err)
}
return manifestBuffer.String()
} }
func iosPlatformFor(target string) string { func iosPlatformFor(target string) string {
@@ -386,7 +490,7 @@ func archiveIOS(tmpDir, target, frameworkRoot string, bi *buildInfo) error {
frameworkDir := filepath.Join(frameworkRoot, "Versions", "A") frameworkDir := filepath.Join(frameworkRoot, "Versions", "A")
for _, dir := range []string{"Headers", "Modules"} { for _, dir := range []string{"Headers", "Modules"} {
p := filepath.Join(frameworkDir, dir) p := filepath.Join(frameworkDir, dir)
if err := os.MkdirAll(p, 0755); err != nil { if err := os.MkdirAll(p, 0o755); err != nil {
return err return err
} }
} }
@@ -457,7 +561,7 @@ func archiveIOS(tmpDir, target, frameworkRoot string, bi *buildInfo) error {
export * export *
}`, framework) }`, framework)
moduleFile := filepath.Join(frameworkDir, "Modules", "module.modulemap") moduleFile := filepath.Join(frameworkDir, "Modules", "module.modulemap")
return os.WriteFile(moduleFile, []byte(module), 0644) return os.WriteFile(moduleFile, []byte(module), 0o644)
} }
func iosCompilerFor(target, arch string, minsdk int) (string, []string, error) { func iosCompilerFor(target, arch string, minsdk int) (string, []string, error) {
@@ -500,7 +604,6 @@ func iosCompilerFor(target, arch string, minsdk int) (string, []string, error) {
return "", nil, err return "", nil, err
} }
cflags := []string{ cflags := []string{
"-fembed-bitcode",
"-arch", allArchs[arch].iosArch, "-arch", allArchs[arch].iosArch,
"-isysroot", sdkPath, "-isysroot", sdkPath,
"-m" + platformOS + "-version-min=" + strconv.Itoa(minsdk), "-m" + platformOS + "-version-min=" + strconv.Itoa(minsdk),
+2 -1
View File
@@ -30,6 +30,7 @@ func (d *JSTestDriver) Start(path string) {
if raceEnabled { if raceEnabled {
d.Skipf("js/wasm doesn't support -race; skipping") d.Skipf("js/wasm doesn't support -race; skipping")
} }
d.Skipf("test fails with \"timed out waiting for a frame to be ready\"")
// First, build the app. // First, build the app.
dir := d.tempDir("gio-endtoend-js") dir := d.tempDir("gio-endtoend-js")
@@ -60,7 +61,7 @@ func (d *JSTestDriver) Start(path string) {
pr, pw := io.Pipe() pr, pw := io.Pipe()
d.Cleanup(func() { pw.Close() }) d.Cleanup(func() { pw.Close() })
d.output = pr d.output = pr
chromedp.ListenTarget(ctx, func(ev interface{}) { chromedp.ListenTarget(ctx, func(ev any) {
switch ev := ev.(type) { switch ev := ev.(type) {
case *runtime.EventConsoleAPICalled: case *runtime.EventConsoleAPICalled:
switch ev.Type { switch ev.Type {
+11 -6
View File
@@ -20,7 +20,7 @@ func buildJS(bi *buildInfo) error {
if out == "" { if out == "" {
out = bi.name out = bi.name
} }
if err := os.MkdirAll(out, 0700); err != nil { if err := os.MkdirAll(out, 0o700); err != nil {
return err return err
} }
cmd := exec.Command( cmd := exec.Command(
@@ -48,7 +48,7 @@ func buildJS(bi *buildInfo) error {
if err != nil { if err != nil {
return err return err
} }
if err := os.WriteFile(filepath.Join(out, filepath.Base(bi.iconPath)), icon, 0600); err != nil { if err := os.WriteFile(filepath.Join(out, filepath.Base(bi.iconPath)), icon, 0o600); err != nil {
return err return err
} }
faviconPath = filepath.Base(bi.iconPath) faviconPath = filepath.Base(bi.iconPath)
@@ -70,7 +70,7 @@ func buildJS(bi *buildInfo) error {
return err return err
} }
if err := os.WriteFile(filepath.Join(out, "index.html"), b.Bytes(), 0600); err != nil { if err := os.WriteFile(filepath.Join(out, "index.html"), b.Bytes(), 0o600); err != nil {
return err return err
} }
@@ -78,9 +78,14 @@ func buildJS(bi *buildInfo) error {
if err != nil { if err != nil {
return err return err
} }
wasmJS := filepath.Join(goroot, "misc", "wasm", "wasm_exec.js") // Location of the wasm_exec.js for go>=1.24
wasmJS := filepath.Join(goroot, "lib", "wasm", "wasm_exec.js")
if _, err := os.Stat(wasmJS); err != nil { if _, err := os.Stat(wasmJS); err != nil {
return fmt.Errorf("failed to find $GOROOT/misc/wasm/wasm_exec.js driver: %v", err) // Location of the wasm_exec.js for go<1.24
wasmJS = filepath.Join(goroot, "misc", "wasm", "wasm_exec.js")
if _, err := os.Stat(wasmJS); err != nil {
return fmt.Errorf("failed to find $GOROOT/misc/wasm/wasm_exec.js driver: %v", err)
}
} }
pkgs, err := packages.Load(&packages.Config{ pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps, Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps,
@@ -169,7 +174,7 @@ const (
</html>` </html>`
// jsSetGo sets the `window.go` variable. // jsSetGo sets the `window.go` variable.
jsSetGo = `(() => { jsSetGo = `(() => {
window.go = {argv: [], env: {}, importObject: {go: {}}}; window.go = {argv: [], env: {}, importObject: {go: {}, gojs: {}}};
const argv = new URLSearchParams(location.search).get("argv"); const argv = new URLSearchParams(location.search).get("argv");
if (argv) { if (argv) {
window.go["argv"] = argv.split(" "); window.go["argv"] = argv.split(" ");
+45 -22
View File
@@ -1,6 +1,7 @@
package main package main
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@@ -34,9 +35,7 @@ func buildMac(tmpDir string, bi *buildInfo) error {
return err return err
} }
if err := builder.setInfo(bi, name); err != nil { builder.setInfo(bi, name)
return fmt.Errorf("can't build the resources: %v", err)
}
for _, arch := range bi.archs { for _, arch := range bi.archs {
tmpDest := filepath.Join(builder.TempDir, filepath.Base(builder.DestDir)) tmpDest := filepath.Join(builder.TempDir, filepath.Base(builder.DestDir))
@@ -89,7 +88,7 @@ func (b *macBuilder) setIcon(path string) (err error) {
} }
out := filepath.Join(b.TempDir, "iconset.iconset") out := filepath.Join(b.TempDir, "iconset.iconset")
if err := os.MkdirAll(out, 0777); err != nil { if err := os.MkdirAll(out, 0o777); err != nil {
return err return err
} }
@@ -107,7 +106,6 @@ func (b *macBuilder) setIcon(path string) (err error) {
{path: "icon_16x16@2x.png", size: 32}, {path: "icon_16x16@2x.png", size: 32},
{path: "icon_16x16.png", size: 16}, {path: "icon_16x16.png", size: 16},
}) })
if err != nil { if err != nil {
return err return err
} }
@@ -123,7 +121,20 @@ func (b *macBuilder) setIcon(path string) (err error) {
return err return err
} }
func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error { func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) {
manifestSrc := struct {
Name string
Bundle string
Version Semver
Schemes []string
}{
Name: name,
Bundle: buildInfo.appID,
Version: buildInfo.version,
Schemes: buildInfo.schemes,
}
t, err := template.New("manifest").Parse(`<?xml version="1.0" encoding="UTF-8"?> t, err := template.New("manifest").Parse(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
@@ -137,21 +148,29 @@ func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error {
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>
<true/> <true/>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>BNDL</string>
{{if .Schemes}}
<key>CFBundleURLTypes</key>
<array>
{{range .Schemes}}
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>{{.}}</string>
</array>
</dict>
{{end}}
</array>
{{end}}
</dict> </dict>
</plist>`) </plist>`)
if err != nil { if err != nil {
return err panic(err)
} }
var manifest bufferCoff var manifest bytes.Buffer
if err := t.Execute(&manifest, struct { if err := t.Execute(&manifest, manifestSrc); err != nil {
Name, Bundle string panic(err)
}{
Name: name,
Bundle: buildInfo.appID,
}); err != nil {
return err
} }
b.Manifest = manifest.Bytes() b.Manifest = manifest.Bytes()
@@ -165,24 +184,22 @@ func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error {
<true/> <true/>
</dict> </dict>
</plist>`) </plist>`)
return nil
} }
func (b *macBuilder) buildProgram(buildInfo *buildInfo, binDest string, name string, arch string) error { func (b *macBuilder) buildProgram(buildInfo *buildInfo, binDest string, name string, arch string) error {
for _, path := range []string{"/Contents/MacOS", "/Contents/Resources"} { for _, path := range []string{"/Contents/MacOS", "/Contents/Resources"} {
if err := os.MkdirAll(filepath.Join(binDest, path), 0755); err != nil { if err := os.MkdirAll(filepath.Join(binDest, path), 0o755); err != nil {
return err return err
} }
} }
if len(b.Icons) > 0 { if len(b.Icons) > 0 {
if err := os.WriteFile(filepath.Join(binDest, "/Contents/Resources/icon.icns"), b.Icons, 0755); err != nil { if err := os.WriteFile(filepath.Join(binDest, "/Contents/Resources/icon.icns"), b.Icons, 0o755); err != nil {
return err return err
} }
} }
if err := os.WriteFile(filepath.Join(binDest, "/Contents/Info.plist"), b.Manifest, 0755); err != nil { if err := os.WriteFile(filepath.Join(binDest, "/Contents/Info.plist"), b.Manifest, 0o755); err != nil {
return err return err
} }
@@ -206,7 +223,7 @@ func (b *macBuilder) buildProgram(buildInfo *buildInfo, binDest string, name str
func (b *macBuilder) signProgram(buildInfo *buildInfo, binDest string, name string, arch string) error { func (b *macBuilder) signProgram(buildInfo *buildInfo, binDest string, name string, arch string) error {
options := filepath.Join(b.TempDir, "ent.ent") options := filepath.Join(b.TempDir, "ent.ent")
if err := os.WriteFile(options, b.Entitlements, 0777); err != nil { if err := os.WriteFile(options, b.Entitlements, 0o777); err != nil {
return err return err
} }
@@ -215,6 +232,12 @@ func (b *macBuilder) signProgram(buildInfo *buildInfo, binDest string, name stri
return err return err
} }
// If the key is a provisioning profile use the same signing process as iOS
if filepath.Ext(buildInfo.key) == ".provisionprofile" {
embedded := filepath.Join(binDest, "Contents", "embedded.provisionprofile")
return signApple(buildInfo.appID, b.TempDir, embedded, binDest, []string{buildInfo.key})
}
cmd := exec.Command( cmd := exec.Command(
"codesign", "codesign",
"--deep", "--deep",
+5 -3
View File
@@ -29,18 +29,20 @@ var (
destPath = flag.String("o", "", "output file or directory.\nFor -target ios or tvos, use the .app suffix to target simulators.") destPath = flag.String("o", "", "output file or directory.\nFor -target ios or tvos, use the .app suffix to target simulators.")
appID = flag.String("appid", "", "app identifier (for -buildmode=exe)") appID = flag.String("appid", "", "app identifier (for -buildmode=exe)")
name = flag.String("name", "", "app name (for -buildmode=exe)") name = flag.String("name", "", "app name (for -buildmode=exe)")
version = flag.String("version", "1.0.0.1", "semver app version (for -buildmode=exe) on the form major.minor.patch.versioncode") version = flag.String("version", "1.0.0.1", "semver app version (for -buildmode=exe) on the form major.minor.patch.versioncode. The versioncode is not used for iOS and macOS.")
printCommands = flag.Bool("x", false, "print the commands") printCommands = flag.Bool("x", false, "print the commands")
keepWorkdir = flag.Bool("work", false, "print the name of the temporary work directory and do not delete it when exiting.") keepWorkdir = flag.Bool("work", false, "print the name of the temporary work directory and do not delete it when exiting.")
linkMode = flag.String("linkmode", "", "set the -linkmode flag of the go tool") linkMode = flag.String("linkmode", "", "set the -linkmode flag of the go tool")
extraLdflags = flag.String("ldflags", "", "extra flags to the Go linker") extraLdflags = flag.String("ldflags", "", "extra flags to the Go linker")
extraTags = flag.String("tags", "", "extra tags to the Go tool") extraTags = flag.String("tags", "", "extra tags to the Go tool")
iconPath = flag.String("icon", "", "specify an icon for iOS and Android") iconPath = flag.String("icon", "", "specify an icon for iOS and Android")
signKey = flag.String("signkey", "", "specify the path of the keystore to be used to sign Android apk files.") signKey = flag.String("signkey", "", "specify the path of the keystore (Android) or provisioning profile (macOS or iOS) for signing")
signPass = flag.String("signpass", "", "specify the password to decrypt the signkey.") signPass = flag.String("signpass", "", "specify the password to decrypt the signkey.")
notaryID = flag.String("notaryid", "", "specify the apple id to use for notarization.") notaryID = flag.String("notaryid", "", "specify the apple id to use for notarization.")
notaryPass = flag.String("notarypass", "", "specify app-specific password of the Apple ID to be used for notarization.") notaryPass = flag.String("notarypass", "", "specify app-specific password of the Apple ID to be used for notarization.")
notaryTeamID = flag.String("notaryteamid", "", "specify the team id to use for notarization.") notaryTeamID = flag.String("notaryteamid", "", "specify the team id to use for notarization.")
schemes = flag.String("schemes", "", "specify a list of comma separated URL schemes that the program accepts")
pkgQueries = flag.String("queries", "", "specify a list of comma separated package names used to query other apps on Android.")
) )
func main() { func main() {
@@ -199,7 +201,7 @@ func buildIcons(baseDir, icon string, variants []iconVariant) error {
v := v v := v
resizes.Go(func() (err error) { resizes.Go(func() (err error) {
path := filepath.Join(baseDir, v.path) path := filepath.Join(baseDir, v.path)
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
return err return err
} }
f, err := os.Create(path) f, err := os.Create(path)
+3
View File
@@ -22,6 +22,9 @@ var AndroidPermissions = map[string][]string{
"wakelock": { "wakelock": {
"android.permission.WAKE_LOCK", "android.permission.WAKE_LOCK",
}, },
"microphone": {
"android.permission.RECORD_AUDIO",
},
} }
var AndroidFeatures = map[string][]string{ var AndroidFeatures = map[string][]string{
+1 -1
View File
@@ -174,7 +174,7 @@ func (d *WaylandTestDriver) Screenshot() image.Image {
return img return img
} }
func (d *WaylandTestDriver) swaymsg(args ...interface{}) { func (d *WaylandTestDriver) swaymsg(args ...any) {
strs := []string{"--socket", d.socket} strs := []string{"--socket", d.socket}
for _, arg := range args { for _, arg := range args {
strs = append(strs, fmt.Sprint(arg)) strs = append(strs, fmt.Sprint(arg))
+10 -2
View File
@@ -202,10 +202,18 @@ func (b *windowsBuilder) buildProgram(buildInfo *buildInfo, name string, arch st
dest = filepath.Join(filepath.Dir(b.DestDir), name+"_"+arch+".exe") dest = filepath.Join(filepath.Dir(b.DestDir), name+"_"+arch+".exe")
} }
ldflags := buildInfo.ldflags
if buildInfo.schemes != nil {
ldflags += ` -X "gioui.org/app.schemesURI=` + strings.Join(buildInfo.schemes, ",") + `" `
}
if buildInfo.appID != "" {
ldflags += ` -X "gioui.org/app.ID=` + buildInfo.appID + `" `
}
cmd := exec.Command( cmd := exec.Command(
"go", "go",
"build", "build",
"-ldflags=-H=windowsgui "+buildInfo.ldflags, "-ldflags=-H=windowsgui "+ldflags,
"-tags="+buildInfo.tags, "-tags="+buildInfo.tags,
"-o", dest, "-o", dest,
buildInfo.pkgPath, buildInfo.pkgPath,
@@ -358,7 +366,7 @@ const (
valueText uint16 = 1 valueText uint16 = 1
) )
func newValue(valueType uint16, key string, input interface{}) windowsInfoValue { func newValue(valueType uint16, key string, input any) windowsInfoValue {
v := windowsInfoValue{ v := windowsInfoValue{
Type: valueType, Type: valueType,
Length: 6, Length: 6,
+1 -1
View File
@@ -145,7 +145,7 @@ func (d *X11TestDriver) Screenshot() image.Image {
return img return img
} }
func (d *X11TestDriver) xdotool(args ...interface{}) string { func (d *X11TestDriver) xdotool(args ...any) string {
d.Helper() d.Helper()
strs := make([]string, len(args)) strs := make([]string, len(args))
for i, arg := range args { for i, arg := range args {
+1 -2
View File
@@ -10,6 +10,7 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"go/format"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
@@ -17,8 +18,6 @@ import (
"strings" "strings"
"unicode" "unicode"
"go/format"
"gioui.org/f32" "gioui.org/f32"
) )