├── example
├── appGo
│ └── .keep
├── appiOS
│ ├── .keep
│ ├── iosApp
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── 100.png
│ │ │ │ ├── 114.png
│ │ │ │ ├── 120.png
│ │ │ │ ├── 144.png
│ │ │ │ ├── 152.png
│ │ │ │ ├── 167.png
│ │ │ │ ├── 180.png
│ │ │ │ ├── 20.png
│ │ │ │ ├── 29.png
│ │ │ │ ├── 40.png
│ │ │ │ ├── 50.png
│ │ │ │ ├── 57.png
│ │ │ │ ├── 58.png
│ │ │ │ ├── 60.png
│ │ │ │ ├── 72.png
│ │ │ │ ├── 76.png
│ │ │ │ ├── 80.png
│ │ │ │ ├── 87.png
│ │ │ │ ├── 1024.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── ViewController.swift
│ │ ├── Info.plist
│ │ └── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ └── iosApp.xcodeproj
│ │ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ │ └── project.pbxproj
├── appAndroid
│ ├── libGo
│ │ ├── libs
│ │ │ └── .keep
│ │ ├── src
│ │ │ └── main
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── app
│ │ ├── .gitignore
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── values
│ │ │ │ │ │ ├── dimens.xml
│ │ │ │ │ │ ├── strings.xml
│ │ │ │ │ │ ├── colors.xml
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ │ ├── menu
│ │ │ │ │ │ └── menu_main.xml
│ │ │ │ │ ├── layout
│ │ │ │ │ │ ├── content_main.xml
│ │ │ │ │ │ └── activity_main.xml
│ │ │ │ │ ├── drawable-v24
│ │ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ │ └── drawable
│ │ │ │ │ │ └── ic_launcher_background.xml
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── mycompany
│ │ │ │ │ └── myapplication
│ │ │ │ │ └── MainActivity.java
│ │ │ ├── test
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── mycompany
│ │ │ │ │ └── myapplication
│ │ │ │ │ └── ExampleUnitTest.java
│ │ │ └── androidTest
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── mycompany
│ │ │ │ └── myapplication
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ ├── proguard-rules.pro
│ │ └── build.gradle
│ ├── settings.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradlew.bat
│ └── gradlew
├── libGo
│ └── mycompany
│ │ └── myproject
│ │ ├── go.mod
│ │ ├── go.sum
│ │ ├── main.go
│ │ ├── pkga
│ │ └── stuff.go
│ │ └── pkgb
│ │ └── ugly.go
├── goupw
└── goup.yaml
├── renovate.json
├── .travis.yml
├── go.mod
├── .gitignore
├── versionfile.go
├── go.sum
├── cmd.go
├── magic.go
├── LICENSE
├── zip.go
├── tar.go
├── resources.go
├── logger.go
├── args.go
├── path_test.go
├── goupyaml.go
├── artifactcache.go
├── resources.xml
├── path.go
├── vendormodule.go
├── helper.go
├── README.md
└── goup.go
/example/appGo/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/appiOS/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/appAndroid/libGo/libs/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/appAndroid/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/example/appAndroid/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ":libGo"
2 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
4 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go_import_path: github.com/worldiety/goup
3 | go:
4 | - 1.17.x
5 | - tip
6 |
7 | script:
8 | - go test -race -v
--------------------------------------------------------------------------------
/example/libGo/mycompany/myproject/go.mod:
--------------------------------------------------------------------------------
1 | module mycompany/myproject
2 |
3 | require github.com/worldiety/std v0.0.0-20190505122457-d3def1386692
4 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/worldiety/goup
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/gofrs/flock v0.7.1
7 | gopkg.in/yaml.v2 v2.2.2
8 | )
9 |
--------------------------------------------------------------------------------
/example/appAndroid/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/libGo/mycompany/myproject/go.sum:
--------------------------------------------------------------------------------
1 | github.com/worldiety/std v0.0.0-20190505122457-d3def1386692/go.mod h1:T6Z5yAV0QksJj9h7J2+VGjC228h05pyMcaYDV6iOSOc=
2 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/100.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/144.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/152.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/167.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/20.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/50.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/72.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/76.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/appAndroid/libGo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/worldiety/goup/HEAD/example/appAndroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | My Application
3 | Settings
4 |
5 |
--------------------------------------------------------------------------------
/example/libGo/mycompany/myproject/main.go:
--------------------------------------------------------------------------------
1 | package myproject
2 |
3 | // AnExportedProjectLevelFunc returns a hello message
4 | func AnExportedProjectLevelFunc() string {
5 | return "hello from myproject"
6 | }
7 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/example/appAndroid/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/example/appAndroid/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat May 04 13:48:49 CEST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 | .idea
14 | gomobilebuilder
15 | example/.goup
16 | fatLib-sources.jar
17 | fatLib.aar
18 | builds
19 | example/appAndroid/libGo/build
20 |
--------------------------------------------------------------------------------
/example/libGo/mycompany/myproject/pkga/stuff.go:
--------------------------------------------------------------------------------
1 | package pkga
2 |
3 | // A HelloCallback demonstrates perhaps the coolest gomobile feature
4 | type HelloCallback interface {
5 | // YourName returns a string to say hello to
6 | YourName() string
7 | }
8 |
9 | // NiceCallback takes a callback and invokes it
10 | func NiceCallback(cb HelloCallback) string {
11 | return "hello world at " + cb.YourName()
12 | }
13 |
--------------------------------------------------------------------------------
/versionfile.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | )
7 |
8 | // ReadVersion tries to read the entire file as a string
9 | func ReadVersion(fname string) string {
10 | str, _ := ioutil.ReadFile(fname)
11 | return string(str)
12 | }
13 |
14 | // WriteVersion overwrites the denoted file with the version string
15 | func WriteVersion(fname string, version string) {
16 | _ = ioutil.WriteFile(fname, []byte(version), os.ModePerm)
17 | }
18 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
2 | github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
3 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
4 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
6 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
7 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/test/java/com/mycompany/myapplication/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.mycompany.myapplication;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/example/appiOS/iosApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // iosApp
4 | //
5 | // Created by Christian Maier on 08.05.19.
6 | // Copyright © 2019 Worldiety. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 | var window: UIWindow?
14 |
15 | func application(
16 | _ application: UIApplication,
17 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
18 | ) -> Bool {
19 | // Override point for customization after application launch.
20 | return true
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/example/appAndroid/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.4.0'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/example/appAndroid/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/cmd.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | func main() {
18 |
19 | args := &Args{}
20 | args.Evaluate()
21 |
22 | gp, err := NewGoUp(args)
23 | must(err)
24 |
25 | err = gp.Build()
26 | must(err)
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/magic.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | const version = "0.0.24"
18 | const goUp = "GoUp"
19 | const goup = "goup"
20 | const defaultResourcesURL = "https://raw.githubusercontent.com/worldiety/goup/master/resources.xml"
21 |
--------------------------------------------------------------------------------
/example/appAndroid/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/androidTest/java/com/mycompany/myapplication/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.mycompany.myapplication;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.mycompany.myapplication", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
--------------------------------------------------------------------------------
/example/appAndroid/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.mycompany.myapplication"
7 | minSdkVersion 15
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation project(":libGo")
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation 'com.android.support:appcompat-v7:28.0.0'
25 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
26 | implementation 'com.android.support:design:28.0.0'
27 | testImplementation 'junit:junit:4.12'
28 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
29 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
30 | }
31 |
--------------------------------------------------------------------------------
/example/libGo/mycompany/myproject/pkgb/ugly.go:
--------------------------------------------------------------------------------
1 | package pkgb
2 |
3 | import "github.com/worldiety/std"
4 |
5 | // GetMap1 returns a map[interface]interface{}
6 | func GetMap1() *std.Map {
7 | myIfaceMap := &std.Map{}
8 | myIfaceMap.Put(std.NewBox("hello"), std.NewBox("world"))
9 | myIfaceMap.Put(std.NewBox(4), std.NewBox(6))
10 | return myIfaceMap
11 | }
12 |
13 | // GetMap2 returns a map[string]string
14 | func GetMap2() *std.StrStrMap {
15 | categories := &std.StrStrMap{}
16 | categories.Put("gandalf", "mage")
17 | categories.Put("bilbo", "hobbit")
18 | categories.Put("sam", "hobbit")
19 | categories.Put("gimli", "dwarf")
20 | categories.Put("aragorn", "numenor")
21 | return categories
22 | }
23 |
24 | // GetMap3 returns a map[string]interface{}
25 | func GetMap3() *std.StrMap {
26 | categories := &std.StrMap{}
27 | categories.Put("mages", std.NewBox(3))
28 | categories.Put("bilbo", std.NewBox("hobbit"))
29 | categories.Put("hasHobbits", std.NewBox(true))
30 | categories.Put("pie", std.NewBox(3.14))
31 | return categories
32 | }
33 |
34 | // GetSlice1 returns a string slice
35 | func GetSlice1() *std.StrSlice {
36 | return &std.StrSlice{[]string{"a", "b", "c"}}
37 | }
38 |
--------------------------------------------------------------------------------
/example/appAndroid/libGo/build.gradle:
--------------------------------------------------------------------------------
1 | // convert gradle log level to go 0=Debug, 1=Info, 2=Warn, 3=Error with default fallback to 1=Info
2 | def goLogLevel = [(LogLevel.DEBUG):0, (LogLevel.INFO):1, (LogLevel.WARN):2, (LogLevel.ERROR):3].get(gradle.startParameter.logLevel, 1)
3 | def proc = "./goupw $goLogLevel".execute(null, project.file("."))
4 |
5 | proc.waitForProcessOutput(System.out, System.err)
6 | if (proc.exitValue() != 0) {
7 | throw new RuntimeException("GoUp failed to compile, inspect output")
8 | }
9 |
10 |
11 | apply plugin: 'com.android.library'
12 |
13 | android {
14 | compileSdkVersion 28
15 |
16 |
17 | defaultConfig {
18 | minSdkVersion 15
19 | targetSdkVersion 28
20 | versionCode 1
21 | versionName "1.0"
22 |
23 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
24 |
25 | }
26 |
27 | buildTypes {
28 | release {
29 | minifyEnabled false
30 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
31 | }
32 | }
33 |
34 | }
35 |
36 | dependencies {
37 | api fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
38 | }
39 |
40 |
41 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2019, worldiety GmbH
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // iosApp
4 | //
5 | // Created by Christian Maier on 08.05.19.
6 | // Copyright © 2019 Worldiety. All rights reserved.
7 | //
8 |
9 | import Myproject
10 | import UIKit
11 |
12 | /// Helper class to wrap a closure as MyprojectPkgaHelloCallbackProtocol
13 | class Callback: NSObject, MyprojectPkgaHelloCallbackProtocol {
14 | private let callback: () -> String
15 | init(_ callback: @escaping () -> String) {
16 | self.callback = callback
17 | super.init()
18 | }
19 |
20 | func yourName() -> String {
21 | return callback()
22 | }
23 | }
24 |
25 | class ViewController: UIViewController {
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 |
29 | /// Call a exported go function
30 | let goString = MyprojectMyprojectAnExportedProjectLevelFunc()
31 | print(goString)
32 |
33 | guard let map2 = MyprojectPkgbGetMap2(), let keys = map2.keys() else {
34 | fatalError("Something's Not Quite Right...")
35 | }
36 |
37 | for index in 0.. 0 ? keys.get(keys.len() - 1, error: nil) : ""
44 |
45 | /// Call a exported go function with an callback paramer
46 | let goCallback = MyprojectPkgaNiceCallback(Callback {
47 | lastName
48 | })
49 | print(goCallback)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/zip.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "archive/zip"
19 | "fmt"
20 | "io"
21 | "os"
22 | "path/filepath"
23 | "strings"
24 | )
25 |
26 | // Unzip will decompress a zip archive
27 | func Unzip(src string, dest string) error {
28 |
29 | r, err := zip.OpenReader(src)
30 | if err != nil {
31 | return err
32 | }
33 | defer r.Close()
34 |
35 | for _, f := range r.File {
36 |
37 | // Store filename/path for returning and using later on
38 | fpath := filepath.Join(dest, f.Name)
39 |
40 | // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
41 | if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
42 | return fmt.Errorf("%s: illegal file path", fpath)
43 | }
44 |
45 | if f.FileInfo().IsDir() {
46 | // Make Folder
47 | err = os.MkdirAll(fpath, os.ModePerm)
48 | if err != nil {
49 | return err
50 | }
51 | continue
52 | }
53 |
54 | // Make File
55 | if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
56 | return err
57 | }
58 |
59 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
60 | if err != nil {
61 | return err
62 | }
63 |
64 | rc, err := f.Open()
65 | if err != nil {
66 | return err
67 | }
68 |
69 | _, err = io.Copy(outFile, rc)
70 |
71 | // Close the file without defer to close before next iteration of loop
72 | _ = outFile.Close()
73 | _ = rc.Close()
74 |
75 | if err != nil {
76 | return err
77 | }
78 | }
79 | return nil
80 | }
81 |
--------------------------------------------------------------------------------
/example/goupw:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This script bootstraps the GoUp setup and should always be checked into the vcs repository.
4 | # It downloads the required GoUp version and executes it.
5 |
6 | # Set the version as required.
7 | VERSION="v0.0.16"
8 |
9 | # Set the required targets (e.g. all|gomobile/android|gomobile/ios|gomobile/android:gomobile/ios)
10 | TARGETS="all"
11 |
12 | ################
13 |
14 | LOG_LEVEL=$1
15 |
16 | if [ -z "$LOG_LEVEL" ]
17 | then
18 | LOG_LEVEL=0
19 | fi
20 |
21 | if [[ "$OSTYPE" == "darwin"* ]]; then
22 | if [[ "$(uname -m)" == "x86_64"* ]]; then
23 | osarch="darwin-amd64"
24 | else
25 | osarch="darwin-$(uname -m)"
26 | fi
27 | else
28 | osarch="linux-amd64"
29 | fi
30 |
31 | default="\e[39m"
32 | lightRed="\e[91m"
33 | lightGreen="\e[92m"
34 |
35 | printf "OS: $lightGreen$osarch$default\n"
36 | printf "Wrapper version: $lightGreen$VERSION$default\n"
37 | printf "Targeting: $lightGreen$TARGETS$default\n"
38 |
39 | GOUPDIR=".goup"
40 |
41 | execName="$GOUPDIR$VERSION"
42 | exec="$GOUPDIR/$execName"
43 |
44 | UPDATE_WRAPPER=true
45 |
46 | if [[ -f "$exec" ]]; then
47 | cd $GOUPDIR
48 | shasum -c $execName.sha -s
49 | if [ "$?" = "0" ]; then
50 | printf "Wrapper status:$lightGreen ok$default\n"
51 | UPDATE_WRAPPER=false
52 | else
53 | printf "Wrapper status:$lightRed Not ok$default\n"
54 | rm "$execName.sha"
55 | rm "$execName"
56 | fi
57 | cd ..
58 | fi
59 |
60 | set -e
61 |
62 | if [ "$UPDATE_WRAPPER" = true ]; then
63 | printf "Creating $lightGreen$(pwd)/$GOUPDIR$default.\n"
64 | mkdir -p $GOUPDIR
65 | cd $GOUPDIR
66 | printf "Fetching wrapper version $lightGreen$VERSION$default.\n"
67 | curl "https://cdn.worldiety.org/github.com/worldiety/goup/$VERSION/$osarch/goup" --output $execName
68 | chmod +x $execName
69 | #curl "https://cdn.worldiety.org/github.com/worldiety/goup/$VERSION/$osarch/goup.sha" --output $execName.sha
70 | shasum -a 1 $execName > $execName.sha
71 | cd ..
72 | fi
73 |
74 | buildDir=$(pwd)
75 |
76 | "$exec" -version
77 |
78 | "$exec" -dir "$buildDir" -loglevel "$LOG_LEVEL" -targets "$TARGETS"
79 |
--------------------------------------------------------------------------------
/example/appAndroid/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/tar.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "archive/tar"
19 | "fmt"
20 | "io"
21 | "os"
22 | )
23 |
24 | // UnTar extracts all files from the given stream (tar file) into the given path
25 | func UnTar(tarstream io.Reader, dst Path) error {
26 | tarReader := tar.NewReader(tarstream)
27 |
28 | type symlink struct {
29 | old string
30 | new string
31 | }
32 |
33 | symlinks := make([]symlink, 0)
34 | for true {
35 | header, err := tarReader.Next()
36 |
37 | if err == io.EOF {
38 | break
39 | }
40 |
41 | if err != nil {
42 | return fmt.Errorf("ExtractTar: Next() failed: %s", err.Error())
43 | }
44 |
45 | switch header.Typeflag {
46 | case tar.TypeDir:
47 | if err := os.Mkdir(dst.Add(Path(header.Name)).String(), 0755); err != nil {
48 | return fmt.Errorf("ExtractTar: Mkdir() failed: %s", err.Error())
49 | }
50 | case tar.TypeReg:
51 | outFile, err := os.OpenFile(dst.Add(Path(header.Name)).String(), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode))
52 | if err != nil {
53 | return fmt.Errorf("ExtractTar: Create() failed: %s", err.Error())
54 | }
55 | if _, err := io.Copy(outFile, tarReader); err != nil {
56 | _ = outFile.Close()
57 | return fmt.Errorf("ExtractTar: Copy() failed: %s", err.Error())
58 | }
59 | _ = outFile.Close()
60 | case tar.TypeSymlink:
61 | src := dst.Add(Path(header.Name))
62 | symlinks = append(symlinks, symlink{old: src.String(), new: header.Linkname})
63 |
64 | default:
65 | return fmt.Errorf("ExtractTar: unknown type: %d (%s) in %s", header.Typeflag, string(header.Typeflag), header.Name)
66 | }
67 | }
68 |
69 | // postpone our symlink creation
70 | for _, symlink := range symlinks {
71 |
72 | // for the jdk, the meaning of .. seems to be broken. The information e.g. about the extracted jdk8u212-b03
73 | // directory is lost, perhaps also a bug in the go tar reader?
74 |
75 | logger.Error(Fields{"action": "symlink", "src": symlink.old, "dst": symlink.new})
76 |
77 | }
78 | return nil
79 | }
80 |
--------------------------------------------------------------------------------
/resources.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "encoding/xml"
19 | "fmt"
20 | "io/ioutil"
21 | "runtime"
22 | )
23 |
24 | // A Resource has a unique combination of name, version, os and architecture.
25 | type Resource struct {
26 | // Name of the resource e.g. go
27 | Name string `xml:"name,attr"`
28 |
29 | // URL to download, e.g. https://dl.google.com/go/go1.12.4.darwin-amd64.tar.gz
30 | URL string `xml:"url,attr"`
31 |
32 | // Version of the resource e.g. 1.12.4
33 | Version string `xml:"version,attr"`
34 |
35 | // OS e.g. darwin
36 | OS string `xml:"os,attr"`
37 |
38 | // Arch e.g. amd64
39 | Arch string `xml:"arch,attr"`
40 | }
41 |
42 | func (r Resource) String() string {
43 | return r.Name + "@" + r.Version + "[" + r.OS + "|" + r.Arch + "]"
44 | }
45 |
46 | type resources struct {
47 | XMLName xml.Name `xml:"resources"`
48 | Resources []Resource `xml:"r"`
49 | }
50 |
51 | // Resources is just a slice of resources
52 | type Resources []Resource
53 |
54 | // Get loops over all resources and returns the fitting resource for the current os/arch combination
55 | func (r *Resources) Get(name string, version string) (Resource, error) {
56 | for _, e := range *r {
57 | if e.Name == name && e.Version == version && e.Arch == runtime.GOARCH && e.OS == runtime.GOOS {
58 | return e, nil
59 | }
60 | }
61 | // try to find something unspecific
62 | for _, e := range *r {
63 | if e.Name == name && e.Version == version && e.Arch == "" && e.OS == "" {
64 | return e, nil
65 | }
66 | }
67 |
68 | return Resource{}, fmt.Errorf("no such resource: %s@%s for os=%s and arch=%s", name, version, runtime.GOOS, runtime.GOARCH)
69 | }
70 |
71 | // Load parses a local xml file and replaces the contents of resources
72 | func (r *Resources) Load(fname Path) error {
73 | tmp := &resources{}
74 | *r = make([]Resource, 0)
75 | data, err := ioutil.ReadFile(fname.String())
76 | if err != nil {
77 | return fmt.Errorf("unable to read xml file: %v", err)
78 | }
79 | err = xml.Unmarshal(data, tmp)
80 | if err != nil {
81 | return fmt.Errorf("unable to parse xml: %v", err)
82 | }
83 | *r = tmp.Resources
84 | return nil
85 | }
86 |
--------------------------------------------------------------------------------
/logger.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "fmt"
19 | "sort"
20 | "strings"
21 | "time"
22 | )
23 |
24 | var logger Logger = &defaultLogger{}
25 |
26 | // The LogLevel determines what to log
27 | type LogLevel int
28 |
29 | const (
30 | // Debug level, 0
31 | Debug LogLevel = iota
32 | // Info level, 1
33 | Info
34 | // Warn level, 2
35 | Warn
36 | // Error level, 3
37 | Error
38 | )
39 |
40 | // SetLogger sets the module/package level logger
41 | func SetLogger(log Logger) {
42 | logger = log
43 | }
44 |
45 | // Fields is just a type alias to avoid verbosity while using the logger
46 | type Fields = map[string]interface{}
47 |
48 | // A Logger is just a simple interface which can be easily satisfied by any implementor
49 | type Logger interface {
50 | Debug(fields Fields)
51 | Info(fields Fields)
52 | Warn(fields Fields)
53 | Error(fields Fields)
54 | }
55 |
56 | // the default logger just prints as json into stdout
57 | type defaultLogger struct {
58 | LogLevel LogLevel
59 | }
60 |
61 | func (l *defaultLogger) Debug(fields map[string]interface{}) {
62 | if l.LogLevel > Debug {
63 | return
64 | }
65 | fields["level"] = "DEBUG"
66 | l.log(fields)
67 | }
68 |
69 | func (l *defaultLogger) Info(fields map[string]interface{}) {
70 | if l.LogLevel > Info {
71 | return
72 | }
73 | fields["level"] = "INFO"
74 | l.log(fields)
75 | }
76 |
77 | func (l *defaultLogger) Warn(fields map[string]interface{}) {
78 | if l.LogLevel > Warn {
79 | return
80 | }
81 | fields["level"] = "WARN"
82 | l.log(fields)
83 | }
84 |
85 | func (l *defaultLogger) Error(fields map[string]interface{}) {
86 | if l.LogLevel > Error {
87 | return
88 | }
89 | fields["level"] = "ERROR"
90 | l.log(fields)
91 | }
92 |
93 | func (l *defaultLogger) log(fields map[string]interface{}) {
94 | time := time.Now()
95 | sb := &strings.Builder{}
96 | // 2017-04-20 20:25:42
97 | sb.WriteString(fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d.%03d", time.Year(), time.Month(), time.Day(), time.Hour(), time.Minute(), time.Second(), time.Nanosecond()/1000/1000))
98 | sb.WriteString(" [")
99 | sb.WriteString(fields["level"].(string))
100 | delete(fields, "level")
101 | sb.WriteString("] - ")
102 |
103 | tmp := make([]string, 0)
104 | for k, v := range fields {
105 | tmp = append(tmp, fmt.Sprintf("%s: %v, ", k, v))
106 | }
107 | sort.Strings(tmp)
108 | for _, s := range tmp {
109 | sb.WriteString(s)
110 | }
111 | fmt.Println(sb.String())
112 | }
113 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/java/com/mycompany/myapplication/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.mycompany.myapplication;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.FloatingActionButton;
5 | import android.support.design.widget.Snackbar;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.Toolbar;
8 | import android.view.View;
9 | import android.view.Menu;
10 | import android.view.MenuItem;
11 |
12 | import com.mycompany.myproject.myproject.Myproject;
13 | import com.mycompany.myproject.pkga.HelloCallback;
14 | import com.mycompany.myproject.pkga.Pkga;
15 | import com.mycompany.myproject.pkgb.Pkgb;
16 | import com.mycompany.myproject.std.StrSlice;
17 | import com.mycompany.myproject.std.StrStrMap;
18 |
19 | public class MainActivity extends AppCompatActivity {
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_main);
25 | Toolbar toolbar = findViewById(R.id.toolbar);
26 | setSupportActionBar(toolbar);
27 |
28 | FloatingActionButton fab = findViewById(R.id.fab);
29 | fab.setOnClickListener(new View.OnClickListener() {
30 | @Override
31 | public void onClick(View view) {
32 | System.out.println(Myproject.anExportedProjectLevelFunc());
33 | String fromGo = Pkga.niceCallback(new HelloCallback() {
34 | @Override
35 | public String yourName() {
36 | return "GoUp";
37 | }
38 | });
39 | System.out.println("Hello Mr. " + fromGo);
40 | try {
41 | System.out.println(Pkgb.getMap2().get(Pkgb.getMap2().keys().get(0)));
42 | } catch (Exception e) {
43 | throw new RuntimeException(e);
44 | }
45 | }
46 | });
47 |
48 | StrStrMap map = Pkgb.getMap2();
49 |
50 | for (int i = 0; i < map.keys().len(); i++) {
51 | try {
52 | String key = map.keys().get(i);
53 | String value = map.get(key);
54 | System.out.println(i + " - " + key +": " + value);
55 | } catch (Exception e) {
56 |
57 | }
58 | }
59 | }
60 |
61 | @Override
62 | public boolean onCreateOptionsMenu (Menu menu){
63 | // Inflate the menu; this adds items to the action bar if it is present.
64 | getMenuInflater().inflate(R.menu.menu_main, menu);
65 | return true;
66 | }
67 |
68 | @Override
69 | public boolean onOptionsItemSelected (MenuItem item){
70 | // Handle action bar item clicks here. The action bar will
71 | // automatically handle clicks on the Home/Up button, so long
72 | // as you specify a parent activity in AndroidManifest.xml.
73 | int id = item.getItemId();
74 |
75 | //noinspection SimplifiableIfStatement
76 | if (id == R.id.action_settings) {
77 | return true;
78 | }
79 |
80 | return super.onOptionsItemSelected(item);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/example/goup.yaml:
--------------------------------------------------------------------------------
1 | # The name is used to setup a custom workspace and tools.
2 | # You should not invoke parallel builds for the same project
3 | name: MySuperProject
4 |
5 | # set custom environment variables, which are always applied into the executing environment, just
6 | # like they have been defined before invoking GoUp
7 | variables:
8 | TEST: "HELLO WORLD"
9 | TEST2: "HELLO WORLD"
10 |
11 | # before_script is executing the following commands before the actual build starts. You can use it, to e.g. work around
12 | # authentication problems with go get and git
13 | before_script:
14 | - git config --global url."https://user:password@gitlab.mycompany.com/".insteadOf "https://gitlab.mycompany.com/"
15 |
16 |
17 |
18 | # The build section defines what and how goup should work
19 | build:
20 | # We want a gomobile build, e.g. for ios or android
21 | gomobile:
22 |
23 | # the toolchain section is required to setup a stable gomobile building experience
24 | toolchain:
25 | # which go version?
26 | go: 1.17.8
27 | # which android ndk version?
28 | ndk: r19c
29 | # which android sdk version?
30 | sdk: 4333796
31 | # which java version?
32 | jdk: 8u212b03
33 | # which gomobile version?
34 | gomobile: wdy-v0.0.2
35 |
36 | # The ios section defines how our iOS library is build. This only works on MacOS with XCode installed
37 | ios:
38 | # The gomobile -prefix flag
39 | prefix: Myproject
40 |
41 | # The gomobile -o flag, this will be a folder
42 | out: ./appIOS/iosApp/Myproject.xcframework
43 |
44 | # The gomobile -bundleid flag sets the bundle ID to use with the app.
45 | bundleid:
46 |
47 | # The gomobile -ldflags flag
48 | ldflags:
49 |
50 |
51 | # The android section defines how our android build is executed
52 | android:
53 | # The gomobile -javapkg flag prefixes the generated packages
54 | javapkg: com.mycompany.myproject
55 |
56 | # The gomobile -o flag, this will be an android archive file. You should only ever use
57 | # a single go library in your app. Otherwise there may be some technical issues and
58 | # it also wastes a lot of storage and memory resources in your app.
59 | out: ./appAndroid/libGo/libs/fatLib.aar
60 |
61 | # The gomobile -ldflags flag
62 | ldflags:
63 |
64 |
65 | # The modules section defines a list of all local or remote go modules, which should be included in the build.
66 | # You can have more than one, but probably you only need a single one and want to use
67 | # real go mod dependencies instead.
68 | modules:
69 | - ./libGo/mycompany/myproject
70 |
71 | # The export section defines all exported packages which are passed to gobind by gomobile.
72 | # Gomobile does not generate transitives exports, so you need to declare all
73 | # packages containing types and methods which you want to have bindings for.
74 | # Be careful with name conflicts, because the last part of the package will be used
75 | # to scope the types.
76 | export:
77 | # contains handsome wrappers to allow passing unsupported types (interfaces, maps, slices)
78 | # through gomobile. In our case pkgb wants to export those types.
79 | # our actual local packages
80 | - mycompany/myproject
81 | - mycompany/myproject/pkga
82 | - mycompany/myproject/pkgb
83 | - github.com/worldiety/std
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "40.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "60.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "29.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "58.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "87.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "80.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "120.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "57x57",
47 | "idiom" : "iphone",
48 | "filename" : "57.png",
49 | "scale" : "1x"
50 | },
51 | {
52 | "size" : "57x57",
53 | "idiom" : "iphone",
54 | "filename" : "114.png",
55 | "scale" : "2x"
56 | },
57 | {
58 | "size" : "60x60",
59 | "idiom" : "iphone",
60 | "filename" : "120.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "60x60",
65 | "idiom" : "iphone",
66 | "filename" : "180.png",
67 | "scale" : "3x"
68 | },
69 | {
70 | "size" : "20x20",
71 | "idiom" : "ipad",
72 | "filename" : "20.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "20x20",
77 | "idiom" : "ipad",
78 | "filename" : "40.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "29x29",
83 | "idiom" : "ipad",
84 | "filename" : "29.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "29x29",
89 | "idiom" : "ipad",
90 | "filename" : "58.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "40x40",
95 | "idiom" : "ipad",
96 | "filename" : "40.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "40x40",
101 | "idiom" : "ipad",
102 | "filename" : "80.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "50x50",
107 | "idiom" : "ipad",
108 | "filename" : "50.png",
109 | "scale" : "1x"
110 | },
111 | {
112 | "size" : "50x50",
113 | "idiom" : "ipad",
114 | "filename" : "100.png",
115 | "scale" : "2x"
116 | },
117 | {
118 | "size" : "72x72",
119 | "idiom" : "ipad",
120 | "filename" : "72.png",
121 | "scale" : "1x"
122 | },
123 | {
124 | "size" : "72x72",
125 | "idiom" : "ipad",
126 | "filename" : "144.png",
127 | "scale" : "2x"
128 | },
129 | {
130 | "size" : "76x76",
131 | "idiom" : "ipad",
132 | "filename" : "76.png",
133 | "scale" : "1x"
134 | },
135 | {
136 | "size" : "76x76",
137 | "idiom" : "ipad",
138 | "filename" : "152.png",
139 | "scale" : "2x"
140 | },
141 | {
142 | "size" : "83.5x83.5",
143 | "idiom" : "ipad",
144 | "filename" : "167.png",
145 | "scale" : "2x"
146 | },
147 | {
148 | "size" : "1024x1024",
149 | "idiom" : "ios-marketing",
150 | "filename" : "1024.png",
151 | "scale" : "1x"
152 | }
153 | ],
154 | "info" : {
155 | "version" : 1,
156 | "author" : "xcode"
157 | }
158 | }
--------------------------------------------------------------------------------
/args.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "flag"
19 | "fmt"
20 | "os"
21 | "path/filepath"
22 | "runtime"
23 | "strings"
24 | )
25 |
26 | // Args contains the arguments which have been used to invoke GoUp
27 | type Args struct {
28 | // The BaseDir is used to resolve paths in goup.yaml
29 | BaseDir Path
30 |
31 | // The goup.yml file to use
32 | BuildFile Path
33 |
34 | // HomeDir is the place where GoUp caches toolchains, projects and workspaces.
35 | HomeDir Path
36 |
37 | // The LogLevel determines what is printed into the console
38 | LogLevel LogLevel
39 |
40 | // ResourcesURL is used to update the external resources list
41 | ResourcesURL string
42 |
43 | // Targets contains the different build targets, e.g. gomobile/android or gomobile/ios
44 | Targets []string
45 |
46 | // ClearWorkspace does not reuse the workspace
47 | ClearWorkspace bool
48 | }
49 |
50 | // Evaluate reads all flags and parses them into the receiver.
51 | // On failures or if help requested it may also print to the console
52 | // and exit
53 | func (a *Args) Evaluate() {
54 | defaultHome, err := os.UserHomeDir()
55 | if err != nil {
56 | defaultHome = "/"
57 | }
58 | defaultHome = filepath.Join(defaultHome, "."+goup)
59 |
60 | overriddenDefaultHome := os.Getenv("GOUP_HOME")
61 | if len(overriddenDefaultHome) > 0 {
62 | defaultHome = overriddenDefaultHome
63 | }
64 |
65 | baseDir := flag.String("dir", CWD(), "Use a custom directory to resolve relative paths from "+goup+".yml.")
66 | buildFile := flag.String("buildFile", "./"+goup+".yaml", "Use a build file to load.")
67 | homeDir := flag.String("home", defaultHome, "Use this as the home directory, where "+goUp+" holds toolchains, projects and workspaces.")
68 | logLevel := flag.Int("loglevel", int(Error), "The LogLevel determines what is printed into the console. 0=Debug, 1=Info, 2=Warn, 3=Error")
69 | resourcesURL := flag.String("resources", defaultResourcesURL, "XML which describes downloadable toolchains")
70 | targets := flag.String("targets", "all", "The targets to build, e.g. gomobile/android or gomobile/ios. Can be concated by :")
71 |
72 | showVersion := flag.Bool("version", false, "Shows the version")
73 | showHelp := flag.Bool("help", false, "Shows this help")
74 | doReset := flag.Bool("reset", false, "Performs a reset, delete the home directory and exits")
75 | doClean := flag.Bool("clean", false, "Removes the project workspace, but keeps toolchains.")
76 |
77 | flag.Parse()
78 | if *showHelp {
79 | flag.PrintDefaults()
80 | os.Exit(0)
81 | }
82 |
83 | if *showVersion {
84 | fmt.Println(goUp + " " + version)
85 | os.Exit(0)
86 | }
87 |
88 | a.BaseDir = Path(*baseDir)
89 | a.BuildFile = Path(*buildFile).Resolve(a.BaseDir)
90 | a.HomeDir = Path(*homeDir)
91 | a.LogLevel = LogLevel(*logLevel)
92 | a.ResourcesURL = *resourcesURL
93 | a.Targets = strings.Split(*targets, ":")
94 | a.ClearWorkspace = *doClean
95 |
96 | logger = &defaultLogger{a.LogLevel}
97 |
98 | logger.Debug(Fields{"Name": goUp, "Version": version, "GOARCH": runtime.GOARCH, "GOOS": runtime.GOOS})
99 | logger.Debug(Fields{"BaseDir": a.BaseDir, "BuildFile": a.BuildFile, "HomeDir": a.HomeDir, "LogLevel": a.LogLevel, "ResourcesURL": a.ResourcesURL, "Targets": a.Targets})
100 |
101 | if *doReset {
102 | err := os.RemoveAll(a.HomeDir.String())
103 | must(err)
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/path_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestEmptyPath(t *testing.T) {
8 | cases := []string{"", "/"}
9 | for _, str := range cases {
10 | p := Path(str)
11 | if p.NameCount() != 0 {
12 | t.Fatal("expected 0 but got", p.NameCount())
13 | }
14 |
15 | if len(p.Names()) != 0 {
16 | t.Fatal("expected 0 but got", len(p.Names()))
17 | }
18 |
19 | if p.Parent().NameCount() != 0 {
20 | t.Fatal("expected 0 but got", p.Parent().NameCount())
21 | }
22 |
23 | if p.String() != "/" {
24 | t.Fatal("expected / but got", p.String())
25 | }
26 | }
27 | }
28 |
29 | func Test1Path(t *testing.T) {
30 | cases := []string{"a", "/a", "a/", "/a/"}
31 | for _, str := range cases {
32 | p := Path(str)
33 | if p.NameCount() != 1 {
34 | t.Fatal("expected 1 but got", p.NameCount(), " => "+str)
35 | }
36 |
37 | if len(p.Names()) != 1 {
38 | t.Fatal("expected 1 but got", len(p.Names()), " => "+str)
39 | }
40 |
41 | if p.NameAt(0) != "a" {
42 | t.Fatal("expected a but got", p.NameAt(0))
43 | }
44 |
45 | if p.Parent().NameCount() != 0 {
46 | t.Fatal("expected 0 but got", p.Parent().NameCount())
47 | }
48 |
49 | if p.String() != "/a" {
50 | t.Fatal("expected /a but got", p.String())
51 | }
52 | }
53 | }
54 |
55 | func Test2Path(t *testing.T) {
56 | cases := []string{"a/b", "/a/b", "a/b", "/a/b/"}
57 | for _, str := range cases {
58 | p := Path(str)
59 | if p.NameCount() != 2 {
60 | t.Fatal("expected 1 but got", p.NameCount(), " => "+str)
61 | }
62 |
63 | if len(p.Names()) != 2 {
64 | t.Fatal("expected 1 but got", len(p.Names()), " => "+str)
65 | }
66 |
67 | if p.NameAt(0) != "a" {
68 | t.Fatal("expected a but got", p.NameAt(0))
69 | }
70 |
71 | if p.NameAt(1) != "b" {
72 | t.Fatal("expected b but got", p.NameAt(1))
73 | }
74 |
75 | if p.Parent().NameCount() != 1 {
76 | t.Fatal("expected 1 but got", p.Parent().NameCount())
77 | }
78 |
79 | if p.String() != "/a/b" {
80 | t.Fatal("expected /a/b but got", p.String())
81 | }
82 | }
83 | }
84 |
85 | func TestModPath(t *testing.T) {
86 | p := ConcatPaths("a/b/", "/c")
87 | if p.String() != "/a/b/c" {
88 | t.Fatal("expected /a/b/c but got", p)
89 | }
90 |
91 | p = p.Child("d")
92 | if p.String() != "/a/b/c/d" {
93 | t.Fatal("expected /a/b/c/d but got", p)
94 | }
95 |
96 | p = p.TrimPrefix(Path("a/b/c"))
97 | if p.String() != "/d" {
98 | t.Fatal("expected /d but got", p)
99 | }
100 |
101 | p = p.Child("/x/y/z")
102 | if p.String() != "/d/x/y/z" {
103 | t.Fatal("expected /d/x/y/z but got", p)
104 | }
105 |
106 | p = ""
107 | p = p.Child("/a/b/c")
108 | if p.String() != "/a/b/c" {
109 | t.Fatal("expected /a/b/c but got", p)
110 | }
111 | }
112 |
113 | func TestPath_Normalize(t *testing.T) {
114 | p := Path("..")
115 | if p.Normalize().String() != "/" {
116 | t.Fatal("expected / but got", p.Normalize().String())
117 | }
118 |
119 | p = Path("../../a")
120 | if p.Normalize().String() != "/a" {
121 | t.Fatal("expected /a but got", p.Normalize().String())
122 | }
123 |
124 | p = Path("a/b/c/..")
125 | if p.Normalize().String() != "/a/b" {
126 | t.Fatal("expected /a/b but got", p.Normalize().String())
127 | }
128 |
129 | p = Path("a/b/c/../d")
130 | if p.Normalize().String() != "/a/b/d" {
131 | t.Fatal("expected /a/b/d but got", p.Normalize().String())
132 | }
133 |
134 | p = Path("a/b/c/../d/./e")
135 | if p.Normalize().String() != "/a/b/d/e" {
136 | t.Fatal("expected /a/b/d/e but got", p.Normalize().String())
137 | }
138 | }
139 |
140 | func TestPath_Name(t *testing.T) {
141 | p := Path("")
142 | if p.Name() != "" {
143 | t.Fatal("expected '' but got", p.Name())
144 | }
145 |
146 | p = Path("/")
147 | if p.Name() != "" {
148 | t.Fatal("expected '' but got", p.Name())
149 | }
150 |
151 | p = Path("/a")
152 | if p.Name() != "a" {
153 | t.Fatal("expected 'a' but got", p.Name())
154 | }
155 |
156 | p = Path("/a/b")
157 | if p.Name() != "b" {
158 | t.Fatal("expected 'b' but got", p.Name())
159 | }
160 | }
161 |
162 | func TestPath_StartsWith(t *testing.T) {
163 | p := Path("")
164 | if !p.StartsWith("") {
165 | t.Fatal("expected to start with ''")
166 | }
167 |
168 | p = Path("a")
169 | if !p.StartsWith("/a") {
170 | t.Fatal("expected to start with '/a'")
171 | }
172 |
173 | p = Path("a/b/c/d")
174 | if !p.StartsWith("/a") {
175 | t.Fatal("expected to start with '/a'")
176 | }
177 | }
178 |
179 | func TestPath_EndsWith(t *testing.T) {
180 | p := Path("")
181 | if !p.EndsWith("") {
182 | t.Fatal("expected to end with ''")
183 | }
184 |
185 | p = Path("a")
186 | if !p.EndsWith("/a") {
187 | t.Fatal("expected to end with '/a'")
188 | }
189 |
190 | p = Path("a/b/c/d")
191 | if !p.EndsWith("/d") {
192 | t.Fatal("expected to end with '/d'")
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/goupyaml.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "encoding/json"
19 | "fmt"
20 | "io/ioutil"
21 |
22 | "gopkg.in/yaml.v2"
23 | )
24 |
25 | // A ModuleSpecifier is either a relative path (e.g. ./my/module) or absolute (e.g. /home/usr/my/mod)
26 | // or remote (e.g. github.com/my/mod).
27 | type ModuleSpecifier string
28 |
29 | // A GoModuleName is either an actual name of a go module as defined in go.mod
30 | // or a package name in a GOPATH
31 | type GoModuleName string
32 |
33 | // A GoUpConfiguration is parsed from the goup.yaml file and specifies various build configurations.
34 | type GoUpConfiguration struct {
35 | // The Name is used to setup a custom workspace and tools.
36 | // You should not invoke parallel builds for the same project
37 | Name string
38 |
39 | // Variables set custom environment variables, which are always applied into the executing environment, just
40 | // like they have been defined before invoking GoUp
41 | Variables map[string]string
42 |
43 | // Before_script is executing the following commands before the actual build starts. You can use it, to e.g. work around
44 | // authentication problems with go get and git
45 | Before_script []string
46 |
47 | // The build section defines what and how goup should work
48 | Build *Build
49 | }
50 |
51 | // The Build section defines what and how goup should work
52 | type Build struct {
53 | Gomobile *BuildGomobile
54 | }
55 |
56 | // The BuildGomobile build, e.g. for ios or android
57 | type BuildGomobile struct {
58 | // the toolchain section is required to setup a stable gomobile building experience
59 | Toolchain BuildGomobileToolchain
60 | // The ios section defines how our iOS library is build. This only works on MacOS with XCode installed
61 | Ios *Ios
62 | // The android section defines how our android build is executed
63 | Android *Android
64 |
65 | Modules []ModuleSpecifier
66 |
67 | // The export section defines all exported packages which are passed to gobind by gomobile.
68 | // Gomobile does not generate transitives exports, so you need to declare all
69 | // packages containing types and methods which you want to have bindings for.
70 | // Be careful with name conflicts, because the last part of the package will be used
71 | // to scope the types.
72 | Export []string
73 | }
74 |
75 | // The BuildGomobileToolchain section is required to setup a stable gomobile building experience
76 | type BuildGomobileToolchain struct {
77 | // which go version? e.g. 1.12.4
78 | Go string
79 | // which android ndk version? e.g. r19c
80 | Ndk string
81 | // which android sdk version? e.g. 4333796
82 | Sdk string
83 | // which java jdk version? e.g. 8u212b03
84 | Jdk string
85 | // which gomobile version? e.g. wdy-v0.0.1
86 | Gomobile string
87 | }
88 |
89 | // The Ios section defines how our iOS library is build. This only works on MacOS with XCode installed
90 | type Ios struct {
91 | // The gomobile -prefix flag
92 | Prefix string
93 | // The gomobile -o flag, this will be a folder
94 | Out Path
95 | // The gomobile -bundleid flag sets the bundle ID to use with the app.
96 | Bundleid string
97 | // The gomobile -ldflags flag
98 | Ldflags string
99 | // The disabled flag can be used to declare but disable this build
100 | Disabled bool
101 | }
102 |
103 | // The Android section defines how our android build is executed
104 | type Android struct {
105 | // The gomobile -javapkg flag prefixes the generated packages
106 | Javapkg string
107 | // The gomobile -o flag, this will be an aar file
108 | Out Path
109 | // The gomobile -ldflags flag
110 | Ldflags string
111 | }
112 |
113 | // Load reads a build.yaml file into the receiver
114 | func (c *GoUpConfiguration) Load(file Path) error {
115 | data, err := ioutil.ReadFile(file.String())
116 | if err != nil {
117 | return fmt.Errorf("failed to load GoUpConfiguration from %s: %v", file, err)
118 | }
119 | err = yaml.Unmarshal([]byte(data), c)
120 | if err != nil {
121 | return fmt.Errorf("failed to parse GoUpConfiguration from %s: %v", file, err)
122 | }
123 | return nil
124 | }
125 |
126 | func (c *GoUpConfiguration) String() string {
127 | data, _ := json.Marshal(c)
128 | return string(data)
129 | }
130 |
--------------------------------------------------------------------------------
/artifactcache.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "crypto/sha1"
19 | "encoding/base64"
20 | "encoding/json"
21 | "fmt"
22 | "io/ioutil"
23 | "os"
24 | "strings"
25 | )
26 |
27 | // A ArtifactCache contains information about a build, to decide if it should get compiled again.
28 | // Actually these are just the program arguments.
29 | //
30 | // Calculating the hash is exact but very expensive, we should use a file tree with last mod time stamps.
31 | type ArtifactCache struct {
32 | // The sha sum of all input files
33 | InHash string
34 |
35 | // The sha sum of the output
36 | OutHash string
37 | }
38 |
39 | // calculateInHash takes all input files, like *.go and go.mod and gomobile.build. We do not want to check
40 | // remote changes, which may cause unstable builds, but that is a user problem, not ours.
41 | func (g *GoUp) calculateInHash() string {
42 | extensions := []string{".go", "go.mod", "go.sum"}
43 | hasher := sha1.New()
44 | hasher.Write(sloppyBytes(ioutil.ReadFile(g.args.BuildFile.String())))
45 | for _, modPath := range g.config.Build.Gomobile.Modules {
46 | resolvedPath := Path(modPath).Resolve(g.args.BaseDir)
47 | if !resolvedPath.Exists() {
48 | // this is the external case, we will never check that for performance reasons
49 | continue
50 | }
51 | files, err := ListFiles(resolvedPath.String())
52 | if err != nil {
53 | panic(err)
54 | }
55 | for _, file := range files {
56 | acceptable := false
57 | for _, ext := range extensions {
58 | if strings.HasSuffix(file, ext) {
59 | acceptable = true
60 | break
61 | }
62 | }
63 | if acceptable {
64 | logger.Debug(Fields{"in-artifact": file})
65 |
66 | hasher.Write(sloppyBytes(ioutil.ReadFile(file)))
67 | }
68 | }
69 |
70 | }
71 | hash := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
72 | logger.Debug(Fields{"in-hash": hash})
73 | return hash
74 | }
75 |
76 | // calculateOutHash takes the generated out files to hash them. Absent files are hashed as 0 bytes.
77 | func (g *GoUp) calculateOutHash() string {
78 | extensions := []string{".h", ".plist", ".modulemap"}
79 | hasher := sha1.New()
80 | if g.hasAndroidBuild() {
81 | outFile := g.config.Build.Gomobile.Android.Out.Resolve(g.args.BaseDir)
82 | // we need to hash the file, to avoid rebuilding failures when switching android build on and the dir is missing
83 | hasher.Write([]byte(outFile.String()))
84 | hasher.Write(sloppyBytes(ioutil.ReadFile(outFile.String())))
85 | }
86 | if g.hasIosBuild() {
87 | outFolder := g.config.Build.Gomobile.Ios.Out.Resolve(g.args.BaseDir)
88 | // we need to hash the folder, to avoid rebuilding failures when switching ios build on and the dir is missing
89 | hasher.Write([]byte(outFolder.String()))
90 | files, err := ListFiles(outFolder.String())
91 | if err != nil {
92 | panic(err)
93 | }
94 | for _, file := range files {
95 | acceptable := false
96 | for _, ext := range extensions {
97 | if strings.HasSuffix(file, ext) {
98 | acceptable = true
99 | break
100 | }
101 | }
102 | if acceptable {
103 | logger.Debug(Fields{"in-artifact": file})
104 |
105 | hasher.Write(sloppyBytes(ioutil.ReadFile(file)))
106 | }
107 | }
108 | }
109 | hash := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
110 | logger.Debug(Fields{"out-hash": hash})
111 | return hash
112 | }
113 |
114 | // Save serializes the cache data into json
115 | func (b *ArtifactCache) Save(fname string) error {
116 | data, err := json.Marshal(b)
117 | if err != nil {
118 | return fmt.Errorf("failed to marshal: %v", err)
119 | }
120 | err = ioutil.WriteFile(fname, data, os.ModePerm)
121 | if err != nil {
122 | return fmt.Errorf("failed to save: %v", err)
123 | }
124 | return nil
125 | }
126 |
127 | // Load deserializes the cache data from json and ensures that the cache is cleared.
128 | func (b *ArtifactCache) Load(fname string) error {
129 | data, err := ioutil.ReadFile(fname)
130 | if err != nil {
131 | return fmt.Errorf("failed to load json: %v", err)
132 | }
133 | err = json.Unmarshal(data, b)
134 | if err != nil {
135 | return fmt.Errorf("failed to unmarshal: %v", err)
136 | }
137 | return nil
138 | }
139 |
140 | func sloppyBytes(data []byte, err error) []byte {
141 | if err != nil {
142 | fmt.Println(err)
143 | }
144 | return data
145 | }
146 |
--------------------------------------------------------------------------------
/resources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/path.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "io/ioutil"
19 | "os"
20 | "strings"
21 | )
22 |
23 | // Path is our helper for real paths, copied from our VFS, we should introduce a module for that alone
24 | type Path string
25 |
26 | // StartsWith tests whether the path begins with prefix.
27 | func (p Path) StartsWith(prefix Path) bool {
28 | return strings.HasPrefix(p.String(), prefix.String())
29 | }
30 |
31 | // EndsWith tests whether the path ends with prefix.
32 | func (p Path) EndsWith(suffix Path) bool {
33 | return strings.HasSuffix(p.String(), suffix.String())
34 | }
35 |
36 | // Names splits the path by / and returns all segments as a simple string array.
37 | func (p Path) Names() []string {
38 | tmp := strings.Split(string(p), "/")
39 | cleaned := make([]string, len(tmp))
40 | idx := 0
41 | for _, str := range tmp {
42 | str = strings.TrimSpace(str)
43 | if len(str) > 0 {
44 | cleaned[idx] = str
45 | idx++
46 | }
47 | }
48 | return cleaned[0:idx]
49 | }
50 |
51 | // NameCount returns how many names are included in this path.
52 | func (p Path) NameCount() int {
53 | return len(p.Names())
54 | }
55 |
56 | // NameAt returns the name at the given index.
57 | func (p Path) NameAt(idx int) string {
58 | return p.Names()[idx]
59 | }
60 |
61 | // Name returns the last element in this path or the empty string if this path is empty.
62 | func (p Path) Name() string {
63 | tmp := p.Names()
64 | if len(tmp) > 0 {
65 | return tmp[len(tmp)-1]
66 | }
67 | return ""
68 | }
69 |
70 | // Parent returns the parent path of this path.
71 | func (p Path) Parent() Path {
72 | tmp := p.Names()
73 | if len(tmp) > 0 {
74 | return Path(strings.Join(tmp[:len(tmp)-1], "/"))
75 | }
76 | return ""
77 | }
78 |
79 | // String normalizes the slashes in Path
80 | func (p Path) String() string {
81 | return "/" + strings.Join(p.Names(), "/")
82 | }
83 |
84 | // Child returns a new Path with name appended as a child
85 | func (p Path) Child(name string) Path {
86 | if len(p) == 0 {
87 | if strings.HasPrefix(name, "/") {
88 | return Path(name)
89 | }
90 | return Path("/" + name)
91 |
92 | }
93 | if strings.HasPrefix(name, "/") {
94 | return Path(p.String() + name)
95 | }
96 | return Path(p.String() + "/" + name)
97 | }
98 |
99 | // Exists checks if the (absolute) file exists
100 | func (p Path) Exists() bool {
101 | _, err := os.Stat(p.String())
102 | return err == nil
103 | }
104 |
105 | // IsDir checks if path represents a directory
106 | func (p Path) IsDir() bool {
107 | stat, err := os.Stat(p.String())
108 | if err != nil {
109 | return false
110 | }
111 | return stat.IsDir()
112 | }
113 |
114 | // TrimPrefix returns a path without the prefix
115 | func (p Path) TrimPrefix(prefix Path) Path {
116 | tmp := "/" + strings.TrimPrefix(p.String(), prefix.String())
117 | return Path(tmp)
118 | }
119 |
120 | // Normalize removes any . and .. and returns a Path without those elements.
121 | func (p Path) Normalize() Path {
122 | names := p.Names()
123 | tmp := make([]string, len(names))[0:0]
124 | for _, name := range names {
125 | if name == "." {
126 | continue
127 | }
128 | if name == ".." {
129 | // walk up -> remove last segment
130 | if len(tmp) > 0 {
131 | tmp = tmp[:len(tmp)-1]
132 | }
133 | continue
134 | }
135 | tmp = append(tmp, name)
136 | }
137 | return Path("/" + strings.Join(tmp, "/"))
138 | }
139 |
140 | // Resolve takes the base dir and normalizes this path and returns it.
141 | // E.g.
142 | // 1. "/my/path".Resolve("/some/thing") => /my/path
143 | // 2. "./my/path".Resolve("/some/thing") => /some/thing/my/path
144 | // 3. "my/path".Resolve("/some/thing") => /some/thing/my/path
145 | // 4. "my/path".Resolve("/some/thing") => /some/thing/my/path
146 | // 5. "my/path/../../".Resolve("/some/thing") => /some/thing
147 | func (p Path) Resolve(baseDir Path) Path {
148 | // 1. the absolute filename case
149 | if strings.HasPrefix(string(p), "/") {
150 | return p.Normalize()
151 | }
152 |
153 | // any other case (2-5) needs the prefix of baseDir
154 | return baseDir.Add(p).Normalize()
155 | }
156 |
157 | // Add returns this path concated with the given path. Sadly we have no overloading operator.
158 | func (p Path) Add(path Path) Path {
159 | return ConcatPaths(p, path)
160 | }
161 |
162 | // List returns the list of accessible children
163 | func (p Path) List() (children []Path) {
164 | files, _ := ioutil.ReadDir(p.String())
165 | for _, f := range files {
166 | children = append(children, p.Child(f.Name()))
167 | }
168 | return
169 | }
170 |
171 | // ConcatPaths merges all paths together
172 | func ConcatPaths(paths ...Path) Path {
173 | tmp := make([]string, 0)
174 | for _, path := range paths {
175 | for _, name := range path.Names() {
176 | tmp = append(tmp, name)
177 | }
178 | }
179 | return Path("/" + strings.Join(tmp, "/"))
180 | }
181 |
--------------------------------------------------------------------------------
/example/appAndroid/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/example/appAndroid/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/vendormodule.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "fmt"
19 | "io/ioutil"
20 | "path/filepath"
21 | "sort"
22 | "strconv"
23 | "strings"
24 | )
25 |
26 | // A Version represents a semver version
27 | type Version struct {
28 | Major int64
29 | Minor int64
30 | Micro int64
31 | }
32 |
33 | func (v Version) String() string {
34 | return "v" + strconv.Itoa(int(v.Major)) + "." + strconv.Itoa(int(v.Minor)) + "." + strconv.Itoa(int(v.Micro))
35 | }
36 |
37 | // IsNewer if this version is newer or higher than the other
38 | func (v Version) IsNewer(other Version) bool {
39 | if v.Major > other.Major {
40 | return true
41 | }
42 | if v.Minor > other.Minor {
43 | return true
44 | }
45 | if v.Micro > other.Micro {
46 | return true
47 | }
48 | return false
49 | }
50 |
51 | // ParseSemanticVersion reads strings like v0.1.2 or 0.1.2
52 | func ParseSemanticVersion(str string) (Version, error) {
53 | str = strings.TrimSpace(str)
54 | if strings.HasPrefix(str, "v") {
55 | str = str[1:]
56 | }
57 | msg := "failed to parse version: %s: %v"
58 | tokens := strings.Split(str, ".")
59 | if len(tokens) != 3 {
60 | return Version{}, fmt.Errorf("failed to parse version: %s", str)
61 | }
62 | major, err := strconv.ParseInt(tokens[0], 10, 64)
63 | if err != nil {
64 | return Version{}, fmt.Errorf(msg, str, err)
65 | }
66 | minor, err := strconv.ParseInt(tokens[1], 10, 64)
67 | if err != nil {
68 | return Version{}, fmt.Errorf(msg, str, err)
69 | }
70 |
71 | micro, err := strconv.ParseInt(tokens[2], 10, 64)
72 | if err != nil {
73 | return Version{}, fmt.Errorf(msg, str, err)
74 | }
75 |
76 | return Version{major, minor, micro}, nil
77 | }
78 |
79 | // A VendoredModule represents an entry from modules.txt, as generated by go mod vendor
80 | // e.g.
81 | // # github.com/worldiety/std v0.0.0-20190429141453-4964c97755c6
82 | // github.com/worldiety/std
83 | type VendoredModule struct {
84 | // ModuleName is the actual name of the module
85 | ModuleName string
86 | // Version is the semantic version, usually 0.0.0 if tip of repo should be used
87 | Version Version
88 | // ModuleImport is probably the url from where to get the module data
89 | ModuleImport string
90 |
91 | // Local determines the fully qualified local path
92 | Local Path
93 | }
94 |
95 | // ParseModulesTxT parsed the modules.txt, as generated by go mod-vendor.
96 | // Example:
97 | // # github.com/json-iterator/go v1.1.6
98 | // github.com/json-iterator/go
99 | // # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
100 | // github.com/modern-go/concurrent
101 | // # github.com/modern-go/reflect2 v1.0.1
102 | // github.com/modern-go/reflect2
103 | // # golang.org/x/text v0.3.2
104 | // golang.org/x/text/collate
105 | // golang.org/x/text/language
106 | // golang.org/x/text/internal/colltab
107 | // golang.org/x/text/unicode/norm
108 | // golang.org/x/text/internal/language
109 | // golang.org/x/text/internal/language/compact
110 | // golang.org/x/text/transform
111 | // golang.org/x/text/internal/tag
112 | func ParseModulesTxT(fname string) ([]VendoredModule, error) {
113 | //there is no modules.txt if there are no dependencies, that is valid
114 | if !Path(fname).Exists() {
115 | return nil, nil
116 | }
117 | text, err := ioutil.ReadFile(fname)
118 | if err != nil {
119 | return nil, fmt.Errorf("unable to parse module.txt: %v", err)
120 | }
121 |
122 | res := make([]VendoredModule, 0)
123 |
124 | lines := strings.Split(string(text), "\n")
125 | for i := 0; i < len(lines); {
126 | line := strings.TrimSpace(lines[i])
127 | // skip empty line
128 | if len(line) < 1 {
129 | i++
130 | continue
131 | }
132 |
133 | var modImportURL string
134 | var version Version
135 | // is it a comment?
136 | if strings.HasPrefix(line, "#") {
137 | pathVersionTokens := strings.Split(line, " ")
138 | if len(pathVersionTokens) < 2 {
139 | fmt.Printf("%s: invalid path-version at line %d: %s", fname, i, lines[i])
140 | i++
141 | continue
142 | }
143 |
144 | var versionTokens string
145 | for _, token := range pathVersionTokens {
146 | if token == "#" {
147 | continue
148 | }
149 | if len(modImportURL) == 0 {
150 | modImportURL = token
151 | }
152 |
153 | if len(modImportURL) > 0 && strings.HasPrefix(token, "v") {
154 | versionTokens = token
155 | }
156 | }
157 | if strings.Contains(versionTokens, "-") {
158 | versionTokens = versionTokens[0:strings.Index(versionTokens, "-")]
159 | }
160 | if len(versionTokens) == 0 {
161 | versionTokens = "v0.0.0"
162 | }
163 |
164 | version, err = ParseSemanticVersion(versionTokens)
165 | if err != nil {
166 | i++
167 | fmt.Printf("%s: invalid version string at line %d: %s: %v", fname, i, lines[i], err)
168 | continue
169 | }
170 | i++
171 | }
172 |
173 | modName := lines[i]
174 | if len(modImportURL) == 0 {
175 | modImportURL = modName
176 | }
177 | path := Path(filepath.Dir(fname)).Add(Path(modName))
178 | res = append(res, VendoredModule{Version: version, ModuleName: modName, ModuleImport: modImportURL, Local: path})
179 | i++
180 | }
181 |
182 | return res, nil
183 | }
184 |
185 | // we need to sort the dependencies because the order of a vendored module.txt is not deterministic (at least
186 | // after putting them into a map ;-) but
187 | // because we move them later to the correct location, we need to ensure that shortest paths come first
188 | func asSortedSlice(m map[string]VendoredModule) []VendoredModule {
189 | tmp := make([]VendoredModule, 0)
190 | for _, v := range m {
191 | tmp = append(tmp, v)
192 | }
193 | sort.Sort(byVendoredModuleName(tmp))
194 | return tmp
195 | }
196 |
197 | type byVendoredModuleName []VendoredModule
198 |
199 | func (s byVendoredModuleName) Len() int {
200 | return len(s)
201 | }
202 | func (s byVendoredModuleName) Swap(i, j int) {
203 | s[i], s[j] = s[j], s[i]
204 | }
205 | func (s byVendoredModuleName) Less(i, j int) bool {
206 | return len(s[i].ModuleImport) < len(s[j].ModuleImport)
207 | }
208 |
--------------------------------------------------------------------------------
/helper.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "compress/gzip"
19 | "crypto/sha256"
20 | "encoding/hex"
21 | "fmt"
22 | "io"
23 | "io/ioutil"
24 | "net/http"
25 | "os"
26 | "path"
27 | "path/filepath"
28 | "strconv"
29 | "strings"
30 | "time"
31 | )
32 |
33 | //CWD returns the current working dir or panics if it is unknown
34 | func CWD() string {
35 | dir, err := os.Getwd()
36 | if err != nil {
37 | panic(err)
38 | }
39 | return dir
40 | }
41 |
42 | // mkdirs ensures the path existence
43 | func mkdirs(fname Path) error {
44 | return os.MkdirAll(fname.String(), os.ModePerm)
45 | }
46 |
47 | // must terminates the process if err is not nil
48 | func must(err error) {
49 | if err != nil {
50 | logger.Error(Fields{"err": err})
51 | os.Exit(-1)
52 | }
53 | }
54 |
55 | // getModuleName parses the given file denoted by fname and returns the declared moduleName name
56 | func getModuleName(fname Path) (string, error) {
57 | data, err := ioutil.ReadFile(fname.String())
58 | if err != nil {
59 | return "", err
60 | }
61 | str := string(data)
62 | lines := strings.Split(str, "\n")
63 | for _, line := range lines {
64 | line = strings.TrimSpace(line)
65 | if strings.HasPrefix(line, "module") {
66 | modName := strings.TrimSpace(line[len("module"):])
67 | modName = strings.Replace(modName, "\"", "", -1)
68 | return modName, nil
69 | }
70 | }
71 | return "", fmt.Errorf("go.mod does not contain a module definition")
72 | }
73 |
74 | // CopyDir copies a whole directory recursively
75 | func CopyDir(src string, dst string) error {
76 | var err error
77 | var fds []os.FileInfo
78 | var srcinfo os.FileInfo
79 |
80 | if srcinfo, err = os.Stat(src); err != nil {
81 | return err
82 | }
83 |
84 | if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
85 | return err
86 | }
87 |
88 | if fds, err = ioutil.ReadDir(src); err != nil {
89 | return err
90 | }
91 | for _, fd := range fds {
92 | srcfp := path.Join(src, fd.Name())
93 | dstfp := path.Join(dst, fd.Name())
94 |
95 | if fd.IsDir() {
96 | if err = CopyDir(srcfp, dstfp); err != nil {
97 | fmt.Println(err)
98 | }
99 | } else {
100 | if err = CopyFile(srcfp, dstfp); err != nil {
101 | fmt.Println(err)
102 | }
103 | }
104 | }
105 | return nil
106 | }
107 |
108 | // CopyFile copies a single file from src to dst
109 | func CopyFile(src, dst string) error {
110 | var err error
111 | var srcfd *os.File
112 | var dstfd *os.File
113 | var srcinfo os.FileInfo
114 |
115 | if srcfd, err = os.Open(src); err != nil {
116 | return err
117 | }
118 | defer srcfd.Close()
119 |
120 | if dstfd, err = os.Create(dst); err != nil {
121 | return err
122 | }
123 | defer dstfd.Close()
124 |
125 | if _, err = io.Copy(dstfd, srcfd); err != nil {
126 | return err
127 | }
128 | if srcinfo, err = os.Stat(src); err != nil {
129 | return err
130 | }
131 | return os.Chmod(dst, srcinfo.Mode())
132 | }
133 |
134 | // ListFiles ignores hidden (.*) files but otherwise returns a flat list of absolute filepaths in stable order
135 | func ListFiles(root string) ([]string, error) {
136 | files := make([]string, 0)
137 | err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
138 | if err != nil {
139 | logger.Error(Fields{"msg": "failed to list", "root": root, "path": path, "err": err.Error()})
140 | return nil
141 | }
142 | if strings.HasPrefix(info.Name(), ".") {
143 | if info.IsDir() {
144 | return filepath.SkipDir
145 | }
146 | return nil
147 |
148 | }
149 |
150 | files = append(files, path)
151 | return nil
152 | })
153 | return files, err
154 | }
155 |
156 | // IsEmpty returns true if given string only consists of whitespace chars or is empty
157 | func IsEmpty(str string) bool {
158 | return len(strings.TrimSpace(str)) == 0
159 | }
160 |
161 | // Sha256 calculates a Sha256 hash from the given stromg
162 | func Sha256(str string) string {
163 | t := sha256.Sum256([]byte(str))
164 | return hex.EncodeToString(t[:])
165 | }
166 |
167 | // DownloadFile gets a large file without memory buffering
168 | func DownloadFile(url string, dstFile string) error {
169 | logger.Debug(Fields{"action": "downloading", "url": url, "dst": dstFile})
170 |
171 | out, err := os.Create(dstFile)
172 | if err != nil {
173 | return err
174 | }
175 | defer out.Close()
176 |
177 | // Get the data
178 | resp, err := http.Get(url)
179 | if err != nil {
180 | return err
181 | }
182 | defer resp.Body.Close()
183 |
184 | // Check server response
185 | if resp.StatusCode != http.StatusOK {
186 | return fmt.Errorf("cannot download, bad status: %s", resp.Status)
187 | }
188 |
189 | lastPrinted := time.Now()
190 | pgReader := &progressReader{resp.ContentLength, 0, func(read int64, max int64) {
191 | p := int(float64(read) / float64(max) * 100)
192 | if time.Now().Sub(lastPrinted).Seconds() > 15 {
193 | lastPrinted = time.Now()
194 | logger.Info(Fields{"action": "progress", "status": strconv.Itoa(p) + "%"})
195 | }
196 | }, resp.Body}
197 |
198 | // Writer the body to file
199 | _, err = io.Copy(out, pgReader)
200 | if err != nil {
201 | return err
202 | }
203 |
204 | logger.Debug(Fields{"action": "completed", "url": url, "dst": dstFile})
205 | return nil
206 | }
207 |
208 | func downloadAndUnpack(url string, targetFolder Path) error {
209 | tmpFile := targetFolder.Parent().Child(Sha256(url) + ".tmp")
210 | defer os.Remove(tmpFile.String())
211 |
212 | err := DownloadFile(url, tmpFile.String())
213 | if err != nil {
214 | return err
215 | }
216 | srcFile, err := os.OpenFile(tmpFile.String(), os.O_RDONLY, 0)
217 | if err != nil {
218 | return err
219 | }
220 |
221 | lname := strings.ToLower(url)
222 | if strings.HasSuffix(lname, ".tar.gz") {
223 | uncompressedStream, err := gzip.NewReader(srcFile)
224 | if err != nil {
225 | return fmt.Errorf("gz stream failed: %v", err)
226 | }
227 | return UnTar(uncompressedStream, targetFolder)
228 | }
229 |
230 | if strings.HasSuffix(lname, ".zip") {
231 | return Unzip(tmpFile.String(), targetFolder.String())
232 | }
233 |
234 | return fmt.Errorf("unsupported file format: %s", filepath.Ext(lname))
235 | }
236 |
237 | type progressReader struct {
238 | max int64
239 | current int64
240 | callback func(read int64, max int64)
241 | delegate io.Reader
242 | }
243 |
244 | func (r *progressReader) Read(p []byte) (n int, err error) {
245 | n, err = r.delegate.Read(p)
246 | r.current += int64(n)
247 | if r.callback != nil {
248 | r.callback(r.current, r.max)
249 | }
250 | return n, err
251 | }
252 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GoUp (gomobile modules) [](https://goreportcard.com/report/github.com/worldiety/goup) [](http://godoc.org/github.com/worldiety/goup)
2 | GoUp (pronounced go-up) is an install and make tool which helps to build go modules with
3 | gomobile for android and ios. It contains an automatic versioned toolchain provisioning,
4 | emulated module (vgo) support for gomobile and an build artifact cache.
5 |
6 | The motivation behind this tool is to bring the simplicity into the gomobile android world
7 | again. Today there are a lot of pitfalls to create a working setup, so we
8 | integrated all current workarounds for Linux/amd64 and MacOS/amd64 for a single selected
9 | toolchain into the tool. The name is inspired from *rustup*, the rust toolchain installer.
10 | Like rustup, GoUp will manage different toolchains (Android NDK, SDK, Java JDK and Go)
11 | in different versions, even for distinct projects, to provide you a stable build experience.
12 |
13 | What is still missing is extensive testing, configuration of more NDK, SDK, JDK and Go versions
14 | which are known to be compatible with each other, windows support and versioning of gomobile.
15 | Also the example project layout is created to support a crossplatform project
16 | layout, but still only contains an android and ios project.
17 |
18 | Our perspective is to evaluate the practicality and to develop and support this tool in the
19 | case of success.
20 |
21 | ## Example
22 |
23 | We've create an Android and iOS [example](https://github.com/worldiety/goup/tree/master/example)
24 | for your pleasure. It contains the android module *libGo* which invokes the GoUp wrapper
25 | script (goupw) which in turn downloads the actual GoUp version for your current platform.
26 | Please note, that the *goupw* script is only a crude bash script, were you need to define
27 | the platforms to build (`TARGETS`) and which GoUp version to use (`VERSION`).
28 |
29 | The gradle script in *libGo* builds the go library intentionally in every configuration phase,
30 | which ensures that you have always the valid generated Java API at your fingertips.
31 | Performance wise the impact is reduced, due to the GoUp artifact caching, which avoids
32 | building if nothing has changed, so in subsequent configure calls the process completes within
33 | a few milliseconds.
34 |
35 | To build, you just need to open the project with Android Studio and wait some minutes, until
36 | GoUp has downloaded and installed all required toolchains. To get some 'entertainment', you
37 | have to switch to the *Build Output* window and toggle the view in Android Studio.
38 |
39 | In general you should not check in the generated artifacts, because the generated aar
40 | file may not be deterministic (bitwise).
41 |
42 | The process in iOS is very comparable and integrates the GoUp compilation phase simply as
43 | a *script phase* in the *build phases* section (just after *Target Dependencies*). Note, that you also
44 | need to disable the bitcode flag in *Build Settings*. The generated framework becomes fairly large, so
45 | a check in may also be inadequate.
46 |
47 | ## how to build
48 |
49 | The core of a GoUp project is the goup.yaml file, which contains all declared versions and related
50 | build information to process your project.
51 |
52 | The build yaml file may look like this:
53 |
54 | ```yaml
55 | # The name is used to setup a custom workspace and tools.
56 | # You should not invoke parallel builds for the same project
57 | name: MySuperProject
58 |
59 | # set custom environment variables, which are always applied into the executing environment, just
60 | # like they have been defined before invoking GoUp
61 | variables:
62 | TEST: "HELLO WORLD"
63 | TEST2: "HELLO WORLD"
64 |
65 | # before_script is executing the following commands before the actual build starts. You can use it, to e.g. work around
66 | # authentication problems with go get and git
67 | before_script:
68 | - git config --global url."https://user:password@gitlab.mycompany.com/".insteadOf "https://gitlab.mycompany.com/"
69 |
70 |
71 | # The build section defines what and how goup should work
72 | build:
73 | # We want a gomobile build, e.g. for ios or android
74 | gomobile:
75 |
76 | # the toolchain section is required to setup a stable gomobile building experience
77 | toolchain:
78 | # which go version?
79 | go: 1.12.4
80 | # which android ndk version?
81 | ndk: r19c
82 | # which android sdk version?
83 | sdk: 4333796
84 | # which java version?
85 | jdk: 8u212b03
86 | # which gomobile version?
87 | gomobile: wdy-v0.0.1
88 |
89 | # The ios section defines how our iOS library is build. This only works on MacOS with XCode installed
90 | ios:
91 | # The gomobile -prefix flag
92 | prefix: MyLib
93 |
94 | # The gomobile -o flag, this will be a folder
95 | out: ./appIOS/MyLib.framework
96 |
97 | # The gomobile -bundleid flag sets the bundle ID to use with the app.
98 | bundleid:
99 |
100 | # The gomobile -ldflags flag
101 | ldflags:
102 |
103 |
104 | # The android section defines how our android build is executed
105 | android:
106 | # The gomobile -javapkg flag prefixes the generated packages
107 | javapkg: com.mycompany.myproject
108 |
109 | # The gomobile -o flag, this will be an android archive file. You should only ever use
110 | # a single go library in your app. Otherwise there may be some technical issues and
111 | # it also wastes a lot of storage and memory resources in your app.
112 | out: ./appAndroid/libGo/libs/fatLib.aar
113 |
114 | # The gomobile -ldflags flag
115 | ldflags:
116 |
117 |
118 | # The modules section defines a list of all local or remote go modules, which should be included in the build.
119 | # You can have more than one, but probably you only need a single one and want to use
120 | # real go mod dependencies instead.
121 | modules:
122 | - ./libGo/mycompany/myproject
123 |
124 | # The export section defines all exported packages which are passed to gobind by gomobile.
125 | # Gomobile does not generate transitives exports, so you need to declare all
126 | # packages containing types and methods which you want to have bindings for.
127 | # Be careful with name conflicts, because the last part of the package will be used
128 | # to scope the types.
129 | export:
130 | # contains handsome wrappers to allow passing unsupported types (interfaces, maps, slices)
131 | # through gomobile. In our case pkgb wants to export those types.
132 | - github.com/worldiety/std
133 | # our actual local packages
134 | - mycompany/myproject
135 | - mycompany/myproject/pkga
136 | - mycompany/myproject/pkgb
137 |
138 |
139 |
140 | ```
141 |
142 | GoUp has a few optional commandline arguments, and also evaluates the environment variable GOUP_HOME.
143 |
144 | ```bash
145 | goup -help
146 | -buildFile string
147 | Use a build file to load. (default "./goup.yaml")
148 | -clean
149 | Removes the project workspace, but keeps toolchains.
150 | -dir string
151 | Use a custom directory to resolve relative paths from goup.yml.
152 | -help
153 | Shows this help
154 | -home string
155 | Use this as the home directory, where GoUp holds toolchains, projects and workspaces.
156 | -loglevel int
157 | The LogLevel determines what is printed into the console. 0=Debug, 1=Info, 2=Warn, 3=Error
158 | -reset
159 | Performs a reset, delete the home directory and exits
160 | -resources string
161 | XML which describes downloadable toolchains (default "https://raw.githubusercontent.com/worldiety/goup/master/resources.xml")
162 | -targets string
163 | The targets to build, e.g. gomobile/android or gomobile/ios. Can be concated by : (default "all")
164 | -version
165 | Shows the version
166 | ```
167 |
168 | You always need an *export* list and every exported module should be declared (at least transitively)
169 | from your *module* projects. All referred dependencies are upgraded and copied into
170 | an artificial go path in `~/.goup//go`, so that gomobile is happy. You can also
171 | define more than one local module, e.g. if you have multiple local dependent or related
172 | modules. But probably it would be better to only ever have a single local module and
173 | refer to external versioned go dependencies.
174 |
175 | Toolchains are installed in `~/.goup/toolchains`, one for each type and version. Also
176 | GoUp uses interprocess filelocks for modifying toolchains and projects, to allow
177 | at least concurrent (but sequentialized) builds without corruptions.
178 |
179 |
180 | ## hint
181 | Important things like maps, slices, derived basic and value types won't work with gomobile.
182 | To mitigate these limitations, you can use the module https://github.com/worldiety/std.
183 | Do not forget to export it, as shown in the example.
184 |
--------------------------------------------------------------------------------
/example/appiOS/iosApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E54A84D72282E85E005D5D07 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54A84D62282E85E005D5D07 /* AppDelegate.swift */; };
11 | E54A84D92282E85E005D5D07 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54A84D82282E85E005D5D07 /* ViewController.swift */; };
12 | E54A84DC2282E85E005D5D07 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E54A84DA2282E85E005D5D07 /* Main.storyboard */; };
13 | E54A84DE2282E860005D5D07 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E54A84DD2282E860005D5D07 /* Assets.xcassets */; };
14 | E54A84E12282E860005D5D07 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E54A84DF2282E860005D5D07 /* LaunchScreen.storyboard */; };
15 | E54A84EC2282EBBB005D5D07 /* Myproject.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E54A84EB2282EBBA005D5D07 /* Myproject.framework */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXFileReference section */
19 | E54A84D32282E85E005D5D07 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
20 | E54A84D62282E85E005D5D07 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
21 | E54A84D82282E85E005D5D07 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
22 | E54A84DB2282E85E005D5D07 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
23 | E54A84DD2282E860005D5D07 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
24 | E54A84E02282E860005D5D07 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
25 | E54A84E22282E860005D5D07 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
26 | E54A84EB2282EBBA005D5D07 /* Myproject.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Myproject.framework; sourceTree = ""; };
27 | /* End PBXFileReference section */
28 |
29 | /* Begin PBXFrameworksBuildPhase section */
30 | E54A84D02282E85E005D5D07 /* Frameworks */ = {
31 | isa = PBXFrameworksBuildPhase;
32 | buildActionMask = 2147483647;
33 | files = (
34 | E54A84EC2282EBBB005D5D07 /* Myproject.framework in Frameworks */,
35 | );
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXFrameworksBuildPhase section */
39 |
40 | /* Begin PBXGroup section */
41 | E54A84CA2282E85E005D5D07 = {
42 | isa = PBXGroup;
43 | children = (
44 | E54A84D52282E85E005D5D07 /* iosApp */,
45 | E54A84D42282E85E005D5D07 /* Products */,
46 | );
47 | sourceTree = "";
48 | };
49 | E54A84D42282E85E005D5D07 /* Products */ = {
50 | isa = PBXGroup;
51 | children = (
52 | E54A84D32282E85E005D5D07 /* iosApp.app */,
53 | );
54 | name = Products;
55 | sourceTree = "";
56 | };
57 | E54A84D52282E85E005D5D07 /* iosApp */ = {
58 | isa = PBXGroup;
59 | children = (
60 | E54A84D62282E85E005D5D07 /* AppDelegate.swift */,
61 | E54A84DD2282E860005D5D07 /* Assets.xcassets */,
62 | E54A84E22282E860005D5D07 /* Info.plist */,
63 | E54A84DF2282E860005D5D07 /* LaunchScreen.storyboard */,
64 | E54A84DA2282E85E005D5D07 /* Main.storyboard */,
65 | E54A84EB2282EBBA005D5D07 /* Myproject.framework */,
66 | E54A84D82282E85E005D5D07 /* ViewController.swift */,
67 | );
68 | path = iosApp;
69 | sourceTree = "";
70 | };
71 | /* End PBXGroup section */
72 |
73 | /* Begin PBXNativeTarget section */
74 | E54A84D22282E85E005D5D07 /* iosApp */ = {
75 | isa = PBXNativeTarget;
76 | buildConfigurationList = E54A84E52282E860005D5D07 /* Build configuration list for PBXNativeTarget "iosApp" */;
77 | buildPhases = (
78 | E54A84E82282E892005D5D07 /* goup */,
79 | E513A6FB2283F0E1001B20B3 /* SwiftLint */,
80 | E54A84CF2282E85E005D5D07 /* Sources */,
81 | E54A84D02282E85E005D5D07 /* Frameworks */,
82 | E54A84D12282E85E005D5D07 /* Resources */,
83 | );
84 | buildRules = (
85 | );
86 | dependencies = (
87 | );
88 | name = iosApp;
89 | productName = iosApp;
90 | productReference = E54A84D32282E85E005D5D07 /* iosApp.app */;
91 | productType = "com.apple.product-type.application";
92 | };
93 | /* End PBXNativeTarget section */
94 |
95 | /* Begin PBXProject section */
96 | E54A84CB2282E85E005D5D07 /* Project object */ = {
97 | isa = PBXProject;
98 | attributes = {
99 | LastSwiftUpdateCheck = 1020;
100 | LastUpgradeCheck = 1020;
101 | ORGANIZATIONNAME = Worldiety;
102 | TargetAttributes = {
103 | E54A84D22282E85E005D5D07 = {
104 | CreatedOnToolsVersion = 10.2.1;
105 | };
106 | };
107 | };
108 | buildConfigurationList = E54A84CE2282E85E005D5D07 /* Build configuration list for PBXProject "iosApp" */;
109 | compatibilityVersion = "Xcode 9.3";
110 | developmentRegion = en;
111 | hasScannedForEncodings = 0;
112 | knownRegions = (
113 | en,
114 | Base,
115 | );
116 | mainGroup = E54A84CA2282E85E005D5D07;
117 | productRefGroup = E54A84D42282E85E005D5D07 /* Products */;
118 | projectDirPath = "";
119 | projectRoot = "";
120 | targets = (
121 | E54A84D22282E85E005D5D07 /* iosApp */,
122 | );
123 | };
124 | /* End PBXProject section */
125 |
126 | /* Begin PBXResourcesBuildPhase section */
127 | E54A84D12282E85E005D5D07 /* Resources */ = {
128 | isa = PBXResourcesBuildPhase;
129 | buildActionMask = 2147483647;
130 | files = (
131 | E54A84E12282E860005D5D07 /* LaunchScreen.storyboard in Resources */,
132 | E54A84DE2282E860005D5D07 /* Assets.xcassets in Resources */,
133 | E54A84DC2282E85E005D5D07 /* Main.storyboard in Resources */,
134 | );
135 | runOnlyForDeploymentPostprocessing = 0;
136 | };
137 | /* End PBXResourcesBuildPhase section */
138 |
139 | /* Begin PBXShellScriptBuildPhase section */
140 | E513A6FB2283F0E1001B20B3 /* SwiftLint */ = {
141 | isa = PBXShellScriptBuildPhase;
142 | buildActionMask = 2147483647;
143 | files = (
144 | );
145 | inputFileListPaths = (
146 | );
147 | inputPaths = (
148 | );
149 | name = SwiftLint;
150 | outputFileListPaths = (
151 | );
152 | outputPaths = (
153 | );
154 | runOnlyForDeploymentPostprocessing = 0;
155 | shellPath = /bin/sh;
156 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint autocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
157 | };
158 | E54A84E82282E892005D5D07 /* goup */ = {
159 | isa = PBXShellScriptBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | );
163 | inputFileListPaths = (
164 | );
165 | inputPaths = (
166 | );
167 | name = goup;
168 | outputFileListPaths = (
169 | );
170 | outputPaths = (
171 | );
172 | runOnlyForDeploymentPostprocessing = 0;
173 | shellPath = /bin/sh;
174 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\ncd ..\n./goupw\n";
175 | };
176 | /* End PBXShellScriptBuildPhase section */
177 |
178 | /* Begin PBXSourcesBuildPhase section */
179 | E54A84CF2282E85E005D5D07 /* Sources */ = {
180 | isa = PBXSourcesBuildPhase;
181 | buildActionMask = 2147483647;
182 | files = (
183 | E54A84D92282E85E005D5D07 /* ViewController.swift in Sources */,
184 | E54A84D72282E85E005D5D07 /* AppDelegate.swift in Sources */,
185 | );
186 | runOnlyForDeploymentPostprocessing = 0;
187 | };
188 | /* End PBXSourcesBuildPhase section */
189 |
190 | /* Begin PBXVariantGroup section */
191 | E54A84DA2282E85E005D5D07 /* Main.storyboard */ = {
192 | isa = PBXVariantGroup;
193 | children = (
194 | E54A84DB2282E85E005D5D07 /* Base */,
195 | );
196 | name = Main.storyboard;
197 | sourceTree = "";
198 | };
199 | E54A84DF2282E860005D5D07 /* LaunchScreen.storyboard */ = {
200 | isa = PBXVariantGroup;
201 | children = (
202 | E54A84E02282E860005D5D07 /* Base */,
203 | );
204 | name = LaunchScreen.storyboard;
205 | sourceTree = "";
206 | };
207 | /* End PBXVariantGroup section */
208 |
209 | /* Begin XCBuildConfiguration section */
210 | E54A84E32282E860005D5D07 /* Debug */ = {
211 | isa = XCBuildConfiguration;
212 | buildSettings = {
213 | ALWAYS_SEARCH_USER_PATHS = NO;
214 | CLANG_ANALYZER_NONNULL = YES;
215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
217 | CLANG_CXX_LIBRARY = "libc++";
218 | CLANG_ENABLE_MODULES = YES;
219 | CLANG_ENABLE_OBJC_ARC = YES;
220 | CLANG_ENABLE_OBJC_WEAK = YES;
221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
222 | CLANG_WARN_BOOL_CONVERSION = YES;
223 | CLANG_WARN_COMMA = YES;
224 | CLANG_WARN_CONSTANT_CONVERSION = YES;
225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
228 | CLANG_WARN_EMPTY_BODY = YES;
229 | CLANG_WARN_ENUM_CONVERSION = YES;
230 | CLANG_WARN_INFINITE_RECURSION = YES;
231 | CLANG_WARN_INT_CONVERSION = YES;
232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
236 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
237 | CLANG_WARN_STRICT_PROTOTYPES = YES;
238 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
239 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
240 | CLANG_WARN_UNREACHABLE_CODE = YES;
241 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
242 | CODE_SIGN_IDENTITY = "iPhone Developer";
243 | COPY_PHASE_STRIP = NO;
244 | DEBUG_INFORMATION_FORMAT = dwarf;
245 | ENABLE_STRICT_OBJC_MSGSEND = YES;
246 | ENABLE_TESTABILITY = YES;
247 | GCC_C_LANGUAGE_STANDARD = gnu11;
248 | GCC_DYNAMIC_NO_PIC = NO;
249 | GCC_NO_COMMON_BLOCKS = YES;
250 | GCC_OPTIMIZATION_LEVEL = 0;
251 | GCC_PREPROCESSOR_DEFINITIONS = (
252 | "DEBUG=1",
253 | "$(inherited)",
254 | );
255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
257 | GCC_WARN_UNDECLARED_SELECTOR = YES;
258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
259 | GCC_WARN_UNUSED_FUNCTION = YES;
260 | GCC_WARN_UNUSED_VARIABLE = YES;
261 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
262 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
263 | MTL_FAST_MATH = YES;
264 | ONLY_ACTIVE_ARCH = YES;
265 | SDKROOT = iphoneos;
266 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
267 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
268 | };
269 | name = Debug;
270 | };
271 | E54A84E42282E860005D5D07 /* Release */ = {
272 | isa = XCBuildConfiguration;
273 | buildSettings = {
274 | ALWAYS_SEARCH_USER_PATHS = NO;
275 | CLANG_ANALYZER_NONNULL = YES;
276 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
278 | CLANG_CXX_LIBRARY = "libc++";
279 | CLANG_ENABLE_MODULES = YES;
280 | CLANG_ENABLE_OBJC_ARC = YES;
281 | CLANG_ENABLE_OBJC_WEAK = YES;
282 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
283 | CLANG_WARN_BOOL_CONVERSION = YES;
284 | CLANG_WARN_COMMA = YES;
285 | CLANG_WARN_CONSTANT_CONVERSION = YES;
286 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
288 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
289 | CLANG_WARN_EMPTY_BODY = YES;
290 | CLANG_WARN_ENUM_CONVERSION = YES;
291 | CLANG_WARN_INFINITE_RECURSION = YES;
292 | CLANG_WARN_INT_CONVERSION = YES;
293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
297 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
298 | CLANG_WARN_STRICT_PROTOTYPES = YES;
299 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
300 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
301 | CLANG_WARN_UNREACHABLE_CODE = YES;
302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
303 | CODE_SIGN_IDENTITY = "iPhone Developer";
304 | COPY_PHASE_STRIP = NO;
305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
306 | ENABLE_NS_ASSERTIONS = NO;
307 | ENABLE_STRICT_OBJC_MSGSEND = YES;
308 | GCC_C_LANGUAGE_STANDARD = gnu11;
309 | GCC_NO_COMMON_BLOCKS = YES;
310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
312 | GCC_WARN_UNDECLARED_SELECTOR = YES;
313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
314 | GCC_WARN_UNUSED_FUNCTION = YES;
315 | GCC_WARN_UNUSED_VARIABLE = YES;
316 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
317 | MTL_ENABLE_DEBUG_INFO = NO;
318 | MTL_FAST_MATH = YES;
319 | SDKROOT = iphoneos;
320 | SWIFT_COMPILATION_MODE = wholemodule;
321 | SWIFT_OPTIMIZATION_LEVEL = "-O";
322 | VALIDATE_PRODUCT = YES;
323 | };
324 | name = Release;
325 | };
326 | E54A84E62282E860005D5D07 /* Debug */ = {
327 | isa = XCBuildConfiguration;
328 | buildSettings = {
329 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
330 | CODE_SIGN_STYLE = Automatic;
331 | DEVELOPMENT_TEAM = QCT86V5J25;
332 | ENABLE_BITCODE = NO;
333 | FRAMEWORK_SEARCH_PATHS = (
334 | "$(inherited)",
335 | "$(PROJECT_DIR)/**",
336 | );
337 | INFOPLIST_FILE = iosApp/Info.plist;
338 | LD_RUNPATH_SEARCH_PATHS = (
339 | "$(inherited)",
340 | "@executable_path/Frameworks",
341 | );
342 | PRODUCT_BUNDLE_IDENTIFIER = de.wdy.iosApp.iosApp;
343 | PRODUCT_NAME = "$(TARGET_NAME)";
344 | SWIFT_VERSION = 5.0;
345 | TARGETED_DEVICE_FAMILY = "1,2";
346 | };
347 | name = Debug;
348 | };
349 | E54A84E72282E860005D5D07 /* Release */ = {
350 | isa = XCBuildConfiguration;
351 | buildSettings = {
352 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
353 | CODE_SIGN_STYLE = Automatic;
354 | DEVELOPMENT_TEAM = QCT86V5J25;
355 | ENABLE_BITCODE = NO;
356 | FRAMEWORK_SEARCH_PATHS = (
357 | "$(inherited)",
358 | "$(PROJECT_DIR)/**",
359 | );
360 | INFOPLIST_FILE = iosApp/Info.plist;
361 | LD_RUNPATH_SEARCH_PATHS = (
362 | "$(inherited)",
363 | "@executable_path/Frameworks",
364 | );
365 | PRODUCT_BUNDLE_IDENTIFIER = de.wdy.iosApp.iosApp;
366 | PRODUCT_NAME = "$(TARGET_NAME)";
367 | SWIFT_VERSION = 5.0;
368 | TARGETED_DEVICE_FAMILY = "1,2";
369 | };
370 | name = Release;
371 | };
372 | /* End XCBuildConfiguration section */
373 |
374 | /* Begin XCConfigurationList section */
375 | E54A84CE2282E85E005D5D07 /* Build configuration list for PBXProject "iosApp" */ = {
376 | isa = XCConfigurationList;
377 | buildConfigurations = (
378 | E54A84E32282E860005D5D07 /* Debug */,
379 | E54A84E42282E860005D5D07 /* Release */,
380 | );
381 | defaultConfigurationIsVisible = 0;
382 | defaultConfigurationName = Release;
383 | };
384 | E54A84E52282E860005D5D07 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
385 | isa = XCConfigurationList;
386 | buildConfigurations = (
387 | E54A84E62282E860005D5D07 /* Debug */,
388 | E54A84E72282E860005D5D07 /* Release */,
389 | );
390 | defaultConfigurationIsVisible = 0;
391 | defaultConfigurationName = Release;
392 | };
393 | /* End XCConfigurationList section */
394 | };
395 | rootObject = E54A84CB2282E85E005D5D07 /* Project object */;
396 | }
397 |
--------------------------------------------------------------------------------
/goup.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Torben Schinke
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "fmt"
19 | "io/ioutil"
20 | "net/http"
21 | "os"
22 | "os/exec"
23 | "path/filepath"
24 | "runtime"
25 | "strings"
26 | "time"
27 |
28 | "github.com/gofrs/flock"
29 | )
30 |
31 | // GoUp contains the actual state of the GoUp program
32 | type GoUp struct {
33 | // The program arguments
34 | args *Args
35 | // The parsed config
36 | config *GoUpConfiguration
37 |
38 | // the buildDir is the folder where we collect everything for this project
39 | buildDir Path
40 |
41 | // resources contains a list of valid and known resources, this is always an incomplete list, but
42 | // users may also choose their own list
43 | resources *Resources
44 |
45 | // env contains the environment variables, which may change over time and from step to step.
46 | // Initially this contains exact those variables with which this program has been launched.
47 | env map[string]string
48 |
49 | // cwd is the current working directory and used to launch external programs
50 | cwd Path
51 |
52 | // artifactCache contains information about the last build and is used to avoid unnecessary builds
53 | artifactCache *ArtifactCache
54 | }
55 |
56 | // NewGoUp creates a new GoUp builder
57 | func NewGoUp(args *Args) (*GoUp, error) {
58 | gp := &GoUp{}
59 | gp.args = args
60 | gp.config = &GoUpConfiguration{}
61 | err := gp.config.Load(gp.args.BuildFile)
62 | if err != nil {
63 | return nil, err
64 | }
65 |
66 | logger.Debug(Fields{"buildFile": gp.config.String()})
67 |
68 | gp.buildDir = gp.args.HomeDir.Child(gp.config.Name)
69 | logger.Debug(Fields{"buildDir": gp.buildDir})
70 |
71 | if gp.args.ClearWorkspace {
72 | logger.Debug(Fields{"action": "delete", "path": gp.buildDir})
73 | err := os.RemoveAll(gp.buildDir.String())
74 | if err != nil {
75 | return nil, err
76 | }
77 | os.Exit(0)
78 | }
79 |
80 | must(os.MkdirAll(gp.args.BaseDir.String(), os.ModePerm))
81 | must(os.MkdirAll(gp.args.HomeDir.String(), os.ModePerm))
82 | must(os.MkdirAll(gp.buildDir.String(), os.ModePerm))
83 |
84 | res, err := gp.loadResources()
85 | if err != nil {
86 | return nil, err
87 | }
88 | gp.resources = res
89 | logger.Debug(Fields{"resources": gp.resources})
90 |
91 | gp.env = make(map[string]string)
92 | // insert all custom defined env variables
93 | for k, v := range gp.config.Variables {
94 | gp.setEnv(k, v)
95 | }
96 |
97 | // insert on top of that all env variables from us
98 | for _, e := range os.Environ() {
99 | pair := strings.Split(e, "=")
100 | gp.setEnv(pair[0], pair[1])
101 | }
102 |
103 | return gp, nil
104 | }
105 |
106 | // setEnv set a key/value environment variable
107 | func (g *GoUp) setEnv(key string, val string) {
108 | g.env[key] = val
109 | logger.Debug(Fields{"$export": "env", key: val})
110 | }
111 |
112 | // loadResources only updates once a day or if the ~/.goup/resources.xml is missing
113 | func (g *GoUp) loadResources() (*Resources, error) {
114 | file := g.args.HomeDir.Child("resources.xml")
115 | stat, err := os.Stat(file.String())
116 | if err != nil || time.Now().Sub(stat.ModTime()).Hours() > 24 {
117 | logger.Debug(Fields{"action": "downloading", "url": g.args.ResourcesURL})
118 | _ = os.Remove(file.String())
119 | res, err := http.Get(g.args.ResourcesURL)
120 | if err != nil {
121 | return nil, fmt.Errorf("failed to get resource list: %v", err)
122 | }
123 | defer res.Body.Close()
124 | data, err := ioutil.ReadAll(res.Body)
125 | if err != nil {
126 | return nil, fmt.Errorf("failed to download resource list: %v", err)
127 | }
128 | err = ioutil.WriteFile(file.String(), data, os.ModePerm)
129 | if err != nil {
130 | return nil, fmt.Errorf("failed to save resource list: %v", err)
131 | }
132 | logger.Debug(Fields{"action": "updated", "file": file})
133 | }
134 | res := &Resources{}
135 | logger.Debug(Fields{"action": "parsing", "file": file})
136 | err = res.Load(file)
137 | if err != nil {
138 | return nil, fmt.Errorf("failed to load resources: %v", err)
139 | }
140 | return res, nil
141 | }
142 |
143 | // prepareAndroidSDK is required because the SDK is still not yet functional after downloading.
144 | // Also it saves it things always in the wrong top level folder. Another wtf is that the sdkmanager
145 | // only works with Java 8, even though we have Java 11 today.
146 | func (g *GoUp) prepareAndroidSDK() error {
147 | sdkHome := Path(g.env["ANDROID_HOME"])
148 | if sdkHome.Child("platforms").Exists() {
149 | return nil
150 | }
151 | g.chdir(sdkHome.Child("bin"))
152 | _, err := g.run2("./sdkmanager", []byte("y\n"), "platforms;android-28", "build-tools;28.0.3")
153 | if err != nil {
154 | return err
155 | }
156 |
157 | // just wtf: this version always writes the downloads into the wrong folder. I cannot get why, it does
158 | // so also in the systems commandline, so its broken at all?
159 | // This behavior does not make sense and every other tools expects it inside the sdk
160 | _ = os.Rename(g.toolchainPath().Child(".knownPackages").String(), sdkHome.Child(".knownPackages").String())
161 | _ = os.Rename(g.toolchainPath().Child("licenses").String(), sdkHome.Child("licenses").String())
162 | _ = os.Rename(g.toolchainPath().Child("platforms").String(), sdkHome.Child("platforms").String())
163 | _ = os.Rename(g.toolchainPath().Child("build-tools").String(), sdkHome.Child("build-tools").String())
164 |
165 | return nil
166 | }
167 |
168 | // prepareGomobileToolchain downloads go, ndk and sdk
169 | func (g *GoUp) prepareGomobileToolchain() error {
170 | resources := make([]Resource, 0)
171 |
172 | // go
173 | goVersion := g.config.Build.Gomobile.Toolchain.Go
174 | if IsEmpty(goVersion) {
175 | goVersion = "1.12.4"
176 | }
177 | res, err := g.resources.Get("go", goVersion)
178 | if err != nil {
179 | return fmt.Errorf("cannot prepare android build: %v", err)
180 | }
181 | resources = append(resources, res)
182 |
183 | // gomobile
184 | goMobileVersion := g.config.Build.Gomobile.Toolchain.Gomobile
185 | if IsEmpty(goMobileVersion) {
186 | goMobileVersion = "wdy-v0.0.1"
187 | }
188 | res, err = g.resources.Get("gomobile", goMobileVersion)
189 | if err != nil {
190 | return fmt.Errorf("cannot prepare android build (gomobile version): %v", err)
191 | }
192 | resources = append(resources, res)
193 |
194 | // android ndk
195 | ndkVersion := g.config.Build.Gomobile.Toolchain.Ndk
196 | if IsEmpty(ndkVersion) {
197 | ndkVersion = "r19c"
198 | }
199 | res, err = g.resources.Get("ndk", ndkVersion)
200 | if err != nil {
201 | return fmt.Errorf("cannot prepare android build: %v", err)
202 | }
203 | // if g.hasAndroidBuild() { //TODO seems to be required to also build ios?
204 | resources = append(resources, res)
205 | // }
206 |
207 | // android sdk
208 | sdkVersion := g.config.Build.Gomobile.Toolchain.Sdk
209 | if IsEmpty(sdkVersion) {
210 | sdkVersion = "433796"
211 | }
212 | res, err = g.resources.Get("sdk", sdkVersion)
213 | if err != nil {
214 | return fmt.Errorf("cannot prepare android sdk: %v", err)
215 | }
216 | //if g.hasAndroidBuild() { //TODO seems to be required to also build ios?
217 | resources = append(resources, res)
218 | //}
219 |
220 | // java jdk
221 | jdkVersion := g.config.Build.Gomobile.Toolchain.Jdk
222 | if IsEmpty(jdkVersion) {
223 | sdkVersion = "8u212b03"
224 | }
225 | res, err = g.resources.Get("jdk", jdkVersion)
226 | if err != nil {
227 | return fmt.Errorf("cannot prepare jdk: %v", err)
228 | }
229 | // if g.hasAndroidBuild() { //TODO seems to be required to also build ios?
230 | resources = append(resources, res)
231 | // }
232 |
233 | for _, res := range resources {
234 | targetFolder := g.args.HomeDir.Child("toolchains").Child(res.Name + "-" + res.Version)
235 | if targetFolder.Exists() {
236 | logger.Debug(Fields{"toolchain": res.String(), "status": "exists"})
237 | continue
238 | }
239 |
240 | tmpTargetFolder := Path(targetFolder.String() + ".tmp")
241 | _ = os.RemoveAll(tmpTargetFolder.String())
242 | must(os.MkdirAll(tmpTargetFolder.String(), os.ModePerm))
243 |
244 | err := downloadAndUnpack(res.URL, tmpTargetFolder)
245 | if err != nil {
246 | return fmt.Errorf("failed to provide resource: %s: %v", res.String(), err)
247 | }
248 |
249 | // remove garbage
250 | for _, file := range tmpTargetFolder.List() {
251 | // remove hidden java virus files?
252 | if strings.HasPrefix(file.Name(), ".") && strings.Contains(file.Name(), "jdk") && !file.IsDir() {
253 | _ = os.Remove(file.String())
254 | }
255 |
256 | // finder trash stuff
257 | if file.Name() == ".DS_Store" {
258 | _ = os.Remove(file.String())
259 | }
260 | }
261 |
262 | files, err := ioutil.ReadDir(tmpTargetFolder.String())
263 | if err != nil {
264 | return err
265 | }
266 | if len(files) == 0 {
267 | return fmt.Errorf("no files in resource: %s", res.String())
268 | }
269 |
270 | // just unwrap additional folder
271 | if len(files) == 1 && files[0].IsDir() {
272 | err := os.Rename(tmpTargetFolder.Child(files[0].Name()).String(), targetFolder.String())
273 | if err != nil {
274 | return err
275 | }
276 | } else {
277 | // already at root
278 | err := os.Rename(tmpTargetFolder.String(), targetFolder.String())
279 | if err != nil {
280 | return err
281 | }
282 | }
283 |
284 | _ = os.RemoveAll(tmpTargetFolder.String())
285 |
286 | }
287 |
288 | goRoot := g.args.HomeDir.Child("toolchains").Child("go-" + goVersion)
289 | javaHome := g.args.HomeDir.Child("toolchains").Child("jdk-" + jdkVersion)
290 | if runtime.GOOS == "darwin" {
291 | javaHome = javaHome.Child("Contents").Child("Home")
292 | }
293 |
294 | sdkHome := g.args.HomeDir.Child("toolchains").Child("sdk-" + sdkVersion)
295 |
296 | g.setEnv("GOROOT", goRoot.String())
297 | g.setEnv("GOPATH", g.goPath().String())
298 | g.cleanGoPath()
299 | g.setEnv("PATH",
300 | goRoot.Child("bin").String()+":"+
301 | g.goPath().Child("bin").String()+":"+
302 | javaHome.Child("bin").String()+":"+
303 | sdkHome.String()+":"+
304 | sdkHome.Child("bin").String()+":"+
305 | g.env["PATH"])
306 |
307 | err = os.MkdirAll(g.goPath().String(), os.ModePerm)
308 | if err != nil {
309 | return err
310 | }
311 |
312 | _, _ = g.run("which", "go")
313 | _, _ = g.run("type", "-p", "go")
314 | _, _ = g.run("go", "version")
315 |
316 | g.setEnv("ANDROID_NDK_HOME", g.args.HomeDir.Child("toolchains").Child("ndk-"+ndkVersion).String())
317 | g.setEnv("NDK_PATH", g.env["ANDROID_NDK_HOME"])
318 | g.setEnv("ANDROID_HOME", g.args.HomeDir.Child("toolchains").Child("sdk-"+sdkVersion).String())
319 | g.setEnv("ANDROID_SDK_ROOT", g.env["ANDROID_HOME"])
320 |
321 | g.setEnv("JAVA_HOME", javaHome.String())
322 |
323 | _, _ = g.run("java", "-version")
324 | return nil
325 | }
326 |
327 | func (g *GoUp) cleanGoPath() {
328 |
329 | dedub := make(map[string]string)
330 | home, _ := os.UserHomeDir()
331 | homeGo := filepath.Join(home, "go", "bin")
332 |
333 | tmpPath := g.env["PATH"]
334 | for _, path := range strings.Split(tmpPath, ":") {
335 | switch path {
336 | case "/usr/local/go/bin":
337 | continue
338 | case homeGo:
339 | continue
340 | case homeGo + "/":
341 | continue
342 | default:
343 | dedub[path] = path
344 | }
345 | }
346 | cleanPaths := make([]string, 0)
347 | for k := range dedub {
348 | cleanPaths = append(cleanPaths, k)
349 | }
350 |
351 | g.env["PATH"] = strings.Join(cleanPaths, ":")
352 | }
353 |
354 | // goPath returns the artificial goPath
355 | func (g *GoUp) goPath() Path {
356 | return g.buildDir.Child("go")
357 | }
358 |
359 | // toolchainPath returns the path for the toolchains
360 | func (g *GoUp) toolchainPath() Path {
361 | return g.args.HomeDir.Child("toolchains")
362 | }
363 |
364 | // chdir changes the working directory of GoUp, especially it determines in which context external programs are
365 | // executed
366 | func (g *GoUp) chdir(path Path) {
367 | g.cwd = path
368 | logger.Debug(Fields{"cd": path})
369 | }
370 |
371 | // chmodX invokes chmod +x
372 | func (g *GoUp) chmodX(path Path) error {
373 | _, err := g.run("chmod", "+x", path.String())
374 | return err
375 | }
376 |
377 | func (g *GoUp) run(name string, args ...string) ([]string, error) {
378 | return g.run2(name, nil, args...)
379 | }
380 |
381 | func (g *GoUp) isProtectedEnvKey(key string) bool {
382 | tmp := strings.ToLower(key)
383 | protected := []string{"ssh", "rsa", "passwd", "password", "token"}
384 | for _, p := range protected {
385 | if strings.Contains(tmp, p) {
386 | return true
387 | }
388 | }
389 | return false
390 | }
391 |
392 | func (g *GoUp) run2(name string, pipeTo []byte, args ...string) ([]string, error) {
393 | // we need to assemble the path before execution
394 | // because exec.Command uses LookPath before the environment has been set for execution
395 | err := os.Setenv("PATH", g.env["PATH"])
396 | if err != nil {
397 | panic(err)
398 | }
399 |
400 | cmd := exec.Command(name, args...)
401 |
402 | fields := Fields{}
403 | for k, v := range g.env {
404 | cmd.Env = append(cmd.Env, k+"="+v)
405 | if g.isProtectedEnvKey(k) {
406 | fields[k] = ""
407 | } else {
408 | fields[k] = v
409 | }
410 | }
411 | logger.Debug(fields)
412 |
413 | tmpCmd := name + " "
414 | for _, a := range args {
415 | tmpCmd += a + " "
416 | }
417 | logger.Debug(Fields{"exec": tmpCmd})
418 |
419 | cmd.Dir = g.cwd.String()
420 | if len(pipeTo) != 0 {
421 | pipe, err := cmd.StdinPipe()
422 | if err != nil {
423 | return nil, err
424 | }
425 | _, _ = pipe.Write(pipeTo)
426 | }
427 |
428 | stdoutStderr, err := cmd.CombinedOutput()
429 |
430 | lines := strings.Split(string(stdoutStderr), "\n")
431 | for _, line := range lines {
432 | if err != nil {
433 | logger.Error(Fields{"": line})
434 | } else {
435 | logger.Debug(Fields{"": line})
436 | }
437 | }
438 |
439 | return lines, err
440 | }
441 |
442 | // prepareGomobileFrozen downloads a fixed snapshot of gomobile to avoid the regular build breaking changes
443 | // which happen multiple times per year.
444 | func (g *GoUp) prepareGomobileFrozen() error {
445 | gomobileVersionFile := g.goPath().Child("gomobile.version")
446 | logger.Debug(Fields{"go mobile verson file": gomobileVersionFile})
447 | installedGomobilePathVersion := ReadVersion(gomobileVersionFile.String())
448 | if installedGomobilePathVersion == g.config.Build.Gomobile.Toolchain.Gomobile {
449 | return nil
450 | }
451 |
452 | logger.Debug(Fields{"goPath": g.goPath()})
453 | g.chdir(g.goPath())
454 |
455 | // nuke the mod caches, see also bug https://github.com/golang/go/issues/27455
456 | _, err := g.run("go", "clean", "-modcache")
457 | if err != nil {
458 | return fmt.Errorf("failed to clean go cache: %v", err)
459 | }
460 |
461 | // the workflow expects that the gomobile version has already been downloaded in toolchains
462 | err = os.RemoveAll(g.goPath().String())
463 | if err != nil {
464 | return fmt.Errorf("failed to remove go-path, due to mismatched gomobile version: %v", err)
465 | }
466 |
467 | _ = os.MkdirAll(g.goPath().String(), os.ModePerm)
468 |
469 | // just copy it into the actual workspace
470 | srcPath := g.toolchainPath().Child("gomobile-" + g.config.Build.Gomobile.Toolchain.Gomobile)
471 | dstPath := g.goPath().Child("src")
472 | logger.Debug(Fields{"src path": srcPath, "destPath": dstPath})
473 | err = CopyDir(srcPath.String(), dstPath.String())
474 | if err != nil {
475 | return fmt.Errorf("failed to rename gomobile frozen version: %v", err)
476 | }
477 |
478 | // compile the required tools
479 | _, err = g.run("go", "install", "golang.org/x/mobile/cmd/gobind@latest")
480 | if err != nil {
481 | return fmt.Errorf("failed to install gobind: %v", err)
482 | }
483 |
484 | _, err = g.run("go", "install", "golang.org/x/mobile/cmd/gomobile@latest")
485 | if err != nil {
486 | return fmt.Errorf("failed to install gomobile: %v", err)
487 | }
488 |
489 | // gomobile picks up ndk not anymore from -ndk but from ANDROID_NDK_HOME
490 | // also init actually does nothing anymore with prebuild toolchains, see also
491 | // https://github.com/golang/mobile/commit/ca80213619811c2fbed3ff8345accbd4ba924d45
492 | // With golang 1.18 we have to support the GO111Module which could lead to a major refactoring.
493 | // > this just works with 1.17.8 so far
494 | // _, err = g.run("bin/gomobile", "init")
495 | // if err != nil {
496 | // return fmt.Errorf("failed to init gomobile: %v", err)
497 | // }
498 | WriteVersion(gomobileVersionFile.String(), g.config.Build.Gomobile.Toolchain.Gomobile)
499 | return nil
500 | }
501 |
502 | // prepareGomobile installs gomobile into the gopath, if required
503 | func (g *GoUp) prepareGomobile() error {
504 | if g.goPath().Child("bin").Child("gomobile").Exists() {
505 | return nil
506 | }
507 | g.chdir(g.goPath())
508 | g.setEnv("GO111MODULE", "off")
509 |
510 | logger.Debug(Fields{"action": "installing gomobile"})
511 | _, err := g.run("go", "get", "-u", "golang.org/x/mobile/cmd/gomobile")
512 | if err != nil {
513 | return fmt.Errorf("failed to install gomobile: %v", err)
514 | }
515 |
516 | _, err = g.run("bin/gomobile", "version")
517 | if err != nil {
518 | return fmt.Errorf("failed to invoke gomobile: %v", err)
519 | }
520 |
521 | // compatible hotfix, because go mobile broke again
522 | // gomobile init performs a "go install golang.org/x/mobile/cmd/gobind"
523 | // which fails with "src/golang.org/x/mobile/internal/importers/ast.go:37:2:
524 | // cannot find package "golang.org/x/tools/go/packages"
525 | _, err = g.run("go", "get", "-u", "golang.org/x/mobile/cmd/gobind@latest")
526 | if err != nil {
527 | return fmt.Errorf("failed to get gobind: %v", err)
528 | }
529 |
530 | // gomobile picks up ndk not anymore from -ndk but from ANDROID_NDK_HOME
531 | // also init actually does nothing anymore with prebuild toolchains, see also
532 | // https://github.com/golang/mobile/commit/ca80213619811c2fbed3ff8345accbd4ba924d45
533 | _, err = g.run("bin/gomobile", "init")
534 | if err != nil {
535 | return fmt.Errorf("failed to init gomobile: %v", err)
536 | }
537 | return nil
538 | }
539 |
540 | // copyModulesToWorkspace performs the heavy lifting to get gomobile happy with "modules".
541 | // It evaluates all module dependencies, collects them and copies the maximum resolved (by go mod vendor)
542 | // version into the workspace
543 | func (g *GoUp) copyModulesToWorkspace() error {
544 | dependencies := make(map[string]VendoredModule)
545 | g.chdir(g.goPath())
546 | g.setEnv("GO111MODULE", "on")
547 | resolvedLocalModulePaths := make([]Path, 0)
548 | for _, modPath := range g.config.Build.Gomobile.Modules {
549 | resolvedPath := Path(modPath).Resolve(g.args.BaseDir)
550 |
551 | //non-existing paths are treated as remote sources, they are downloaded directly
552 | if !resolvedPath.Exists() {
553 | // not a local mode, try to go get
554 | _, err := g.run("go", "get", string(modPath))
555 | if err != nil {
556 | return err
557 | }
558 | resolvedPath = g.goPath().Child("pkg").Child("mod").Add(Path(modPath))
559 | }
560 | logger.Debug(Fields{"action": "processing", "path": resolvedPath})
561 | resolvedLocalModulePaths = append(resolvedLocalModulePaths, resolvedPath)
562 |
563 | modName, err := getModuleName(resolvedPath.Child("go.mod"))
564 | if err != nil {
565 | return fmt.Errorf("expected '%s' to have a go.mod file. This is not a go module: %v", resolvedPath, err)
566 | }
567 | logger.Debug(Fields{"name": modName})
568 |
569 | // copy declared go modules into go path
570 | targetDir := g.goPath().Child("src").Add(Path(modName))
571 | logger.Debug(Fields{"action": "removing", "path": targetDir})
572 | err = os.RemoveAll(targetDir.String())
573 | if err != nil {
574 | return fmt.Errorf("failed to clear directory %s: %v", targetDir, err)
575 | }
576 | logger.Debug(Fields{"action": "copying", "path": targetDir})
577 |
578 | err = CopyDir(resolvedPath.String(), targetDir.String())
579 | if err != nil {
580 | return fmt.Errorf("failed to copy directory %s: %v", targetDir, err)
581 | }
582 |
583 | // vendor module dependencies for each module
584 | g.chdir(targetDir)
585 | _, err = g.run("go", "mod", "vendor")
586 | if err != nil {
587 | return fmt.Errorf("failed to vendor module dependencies: %v", err)
588 | }
589 |
590 | modules, err := ParseModulesTxT(targetDir.Child("vendor").Child("modules.txt").String())
591 | if err != nil {
592 | return fmt.Errorf("failed to parse vendor module information: %v", err)
593 | }
594 |
595 | // collected and inspect all modules: upgrade to the largest declared version, causing potential semver conflict
596 | for _, mod := range modules {
597 | logger.Debug(Fields{"action": "found", "module": mod.ModuleName, "version": mod.Version.String()})
598 | dep, ok := dependencies[mod.ModuleName]
599 |
600 | if !ok || mod.Version.IsNewer(dep.Version) {
601 | dependencies[mod.ModuleName] = mod
602 | dep = mod
603 |
604 | logger.Debug(Fields{"action": "upgrade", "module": mod.ModuleName, "version": mod.Version.String()})
605 | }
606 | }
607 | }
608 |
609 | sortedDependencies := asSortedSlice(dependencies)
610 |
611 | // a cleaning run, to purge only once the dependency-roots
612 | for _, dep := range sortedDependencies {
613 | targetDir := g.goPath().Child("src").Add(Path(dep.ModuleName))
614 | err := os.RemoveAll(targetDir.Parent().String())
615 | if err != nil {
616 | return fmt.Errorf("failed to remove module target directory: %v", err)
617 | }
618 | }
619 |
620 | // we collected all dependencies, now copy it into the workspace/gopath
621 | for _, dep := range sortedDependencies {
622 | targetDir := g.goPath().Child("src").Add(Path(dep.ModuleName))
623 | err := os.MkdirAll(targetDir.Parent().String(), os.ModePerm)
624 | if err != nil {
625 | return fmt.Errorf("failed to create module target directory: %v", err)
626 | }
627 | logger.Debug(Fields{"action": "move", "from": dep.Local, "to": targetDir})
628 | if !dep.Local.Exists() {
629 | logger.Debug(Fields{"action": "move", "msg": "already absent, has been moved?", "from": dep.Local})
630 | continue
631 | }
632 | err = os.Rename(dep.Local.String(), targetDir.String())
633 | if err != nil {
634 | return fmt.Errorf("failed to move: %s->%s: %v", dep.Local, targetDir, err)
635 | }
636 | }
637 |
638 | // clear all vendor directories in copied modules
639 | for _, resolvedPath := range resolvedLocalModulePaths {
640 | modName, err := getModuleName(resolvedPath.Child("go.mod"))
641 | if err != nil {
642 | panic(err) // handled above already
643 | }
644 | targetDir := g.goPath().Child("src").Add(Path(modName)).Add("vendor")
645 |
646 | logger.Debug(Fields{"action": "remove", "file": targetDir})
647 | err = os.RemoveAll(targetDir.String())
648 | if err != nil {
649 | return fmt.Errorf("failed to remove: %s: %v", targetDir, err)
650 | }
651 |
652 | }
653 | return nil
654 | }
655 |
656 | // hasTargets checks if the target is defined
657 | func (g *GoUp) hasTarget(target string) bool {
658 | for _, s := range g.args.Targets {
659 | if s == target || s == "all" {
660 | return true
661 | }
662 | }
663 | return false
664 | }
665 |
666 | // hasAndroidBuild returns true if a gomobile android section is defined and enabled
667 | func (g *GoUp) hasAndroidBuild() bool {
668 | return g.config.Build.Gomobile != nil && g.config.Build.Gomobile.Android != nil && g.hasTarget("gomobile/android")
669 | }
670 |
671 | // hasIosBuild returns true if a gomobile ios section is defined and enabled
672 | func (g *GoUp) hasIosBuild() bool {
673 | return g.config.Build.Gomobile != nil && g.config.Build.Gomobile.Ios != nil && g.hasTarget("gomobile/ios")
674 | }
675 |
676 | func (g *GoUp) compileGomobile() error {
677 | logger.Debug(Fields{"action": "compiling gomobile"})
678 | g.chdir(g.goPath())
679 | g.setEnv("GO111MODULE", "off")
680 |
681 | if g.hasAndroidBuild() {
682 | args := []string{"bind", "-v"}
683 |
684 | outFile := g.config.Build.Gomobile.Android.Out.Resolve(g.args.BaseDir)
685 | args = append(args, "-o", outFile.String())
686 |
687 | if len(g.config.Build.Gomobile.Android.Javapkg) > 0 {
688 | args = append(args, "-javapkg", g.config.Build.Gomobile.Android.Javapkg)
689 | }
690 | args = append(args, "-target=android")
691 |
692 | args = append(args, g.config.Build.Gomobile.Export...)
693 | _, err := g.run("bin/gomobile", args...)
694 | if err != nil {
695 | return err
696 | }
697 |
698 | }
699 |
700 | if g.hasIosBuild() {
701 | args := []string{"bind", "-v"}
702 |
703 | if len(g.config.Build.Gomobile.Ios.Out) == 0 {
704 | g.config.Build.Gomobile.Ios.Out = Path("./" + g.config.Name + ".framework")
705 | }
706 | outFile := g.config.Build.Gomobile.Ios.Out.Resolve(g.args.BaseDir)
707 | args = append(args, "-o", outFile.String())
708 |
709 | if len(g.config.Build.Gomobile.Ios.Prefix) > 0 {
710 | args = append(args, "-prefix", g.config.Build.Gomobile.Ios.Prefix)
711 | }
712 | args = append(args, "-target=ios")
713 |
714 | args = append(args, g.config.Build.Gomobile.Export...)
715 | _, err := g.run("bin/gomobile", args...)
716 | if err != nil {
717 | return err
718 | }
719 | }
720 | return nil
721 | }
722 |
723 | // isBuildRequired tries to detect if we need to build again. Because gomobile/cgo compiles really slow we want to
724 | // avoid that in any case (e.g. 30s for "hello world" on a beefy machine) which takes a fraction of a second
725 | // for go itself.
726 | func (g *GoUp) isBuildRequired() bool {
727 | cacheFile := g.buildDir.Child("artifacts.json")
728 | g.artifactCache = &ArtifactCache{}
729 | err := g.artifactCache.Load(cacheFile.String())
730 | if err != nil {
731 | logger.Debug(Fields{"msg": "failed to load the build cache file, could be normal", "err": err.Error()})
732 | return true
733 | }
734 |
735 | inHash := g.calculateInHash()
736 | outHash := g.calculateOutHash()
737 |
738 | if g.artifactCache.InHash != inHash || g.artifactCache.OutHash != outHash {
739 | logger.Debug(Fields{"msg": "build cache indicates file changes"})
740 | return true
741 | }
742 | logger.Debug(Fields{"msg": "no need to build again"})
743 | return false
744 | }
745 |
746 | // updateBuildCache calculates and writes the current in/out hashes
747 | func (g *GoUp) updateBuildCache() {
748 | inHash := g.calculateInHash()
749 | outHash := g.calculateOutHash()
750 |
751 | g.artifactCache.InHash = inHash
752 | g.artifactCache.OutHash = outHash
753 | cacheFile := g.buildDir.Child("artifacts.json")
754 | err := g.artifactCache.Save(cacheFile.String())
755 | if err != nil {
756 | fmt.Println(err)
757 | }
758 | }
759 |
760 | // beforeScript executes the described commands before the build
761 | func (g *GoUp) beforeScript() {
762 | for _, cmd := range g.config.Before_script {
763 | // actually it is not correct to always expect sh to be available
764 | name := "sh"
765 | args := []string{"-c", cmd}
766 | _, err := g.run(name, args...)
767 | must(err)
768 | }
769 | }
770 |
771 | // Build performs the actual build process
772 | func (g *GoUp) Build() error {
773 | started := time.Now()
774 | defer func() {
775 | logger.Info(Fields{"msg": "build done", "time": time.Now().Sub(started).String()})
776 | }()
777 | if !g.isBuildRequired() {
778 | return nil
779 | }
780 |
781 | g.beforeScript()
782 |
783 | // the toolchains can only be modified by one process at once
784 | fileLock := flock.New(g.args.HomeDir.Child("toolchain.lock").String())
785 | err := fileLock.Lock()
786 | if err != nil {
787 | return fmt.Errorf("failed to acquire toolchain lock: %v", err)
788 | }
789 |
790 | {
791 | err = g.prepareGomobileToolchain()
792 | if err != nil {
793 | return fmt.Errorf("failed to prepare gomobile build: %v", err)
794 | }
795 |
796 | err = g.prepareGomobileFrozen()
797 | if err != nil {
798 | return err
799 | }
800 |
801 | err = g.prepareAndroidSDK()
802 | if err != nil {
803 | return fmt.Errorf("failed to init android sdk: %v", err)
804 | }
805 | }
806 |
807 | err = fileLock.Unlock()
808 | if err != nil {
809 | return fmt.Errorf("failed to unlock toolchains: %v", err)
810 | }
811 |
812 | // only one project is allowed to be compiled at time
813 | fileLock = flock.New(g.buildDir.Child("project.lock").String())
814 | err = fileLock.Lock()
815 | if err != nil {
816 | return fmt.Errorf("failed to acquire project lock: %v", err)
817 | }
818 |
819 | {
820 | err = g.copyModulesToWorkspace()
821 | if err != nil {
822 | return err
823 | }
824 |
825 | err = g.compileGomobile()
826 | if err != nil {
827 | return err
828 | }
829 |
830 | g.updateBuildCache()
831 | }
832 |
833 | err = fileLock.Unlock()
834 | if err != nil {
835 | return fmt.Errorf("failed to unlock project: %v", err)
836 | }
837 |
838 | return nil
839 | }
840 |
--------------------------------------------------------------------------------