├── .github
├── dependabot.yml
└── workflows
│ ├── application-build.yml
│ └── docker-env-build.yml
├── .gitignore
├── .gitmodules
├── .idea
├── gradle.xml
├── misc.xml
├── modules.xml
├── modules
│ ├── client
│ │ ├── extender.client.main.iml
│ │ └── extender.client.test.iml
│ └── server
│ │ ├── extender.server.main.iml
│ │ ├── extender.server.test.iml
│ │ └── manifestmergetool
│ │ ├── extender.manifestmergetool.main.iml
│ │ └── extender.manifestmergetool.test.iml
├── runConfigurations
│ ├── Attach_to_java_process_inside_docker.xml
│ └── Run_standalone_Extender.xml
└── vcs.xml
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── LICENSE
├── README.md
├── README_CLIENT.md
├── README_DEBUGGING.md
├── README_SECURITY.md
├── build.gradle
├── client
├── build.gradle
├── scripts
│ └── build_and_copy.sh
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── defold
│ │ └── extender
│ │ └── client
│ │ ├── ExtenderClient.java
│ │ ├── ExtenderClientCache.java
│ │ ├── ExtenderClientException.java
│ │ ├── ExtenderResource.java
│ │ └── FileExtenderResource.java
│ └── test
│ └── java
│ └── com
│ └── defold
│ └── extender
│ └── client
│ └── ExtenderClientTest.java
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── server
├── README.md
├── README_METRICS.md
├── build-docker.sh
├── build.gradle
├── configs
│ ├── application-dev.yml
│ ├── application-influx.yml
│ ├── application-local-dev-android.yml
│ ├── application-local-dev-app.yml
│ ├── application-local-dev.yml
│ ├── application-logging.yml
│ ├── application-metrics.yml
│ ├── application-standalone-dev.yml
│ ├── application-test-app.yml
│ └── extender-logging.xml
├── docker
│ ├── Dockerfile.android-env
│ ├── Dockerfile.android.ndk25-env
│ ├── Dockerfile.base-env
│ ├── Dockerfile.emsdk.2011-env
│ ├── Dockerfile.emsdk.3155-env
│ ├── Dockerfile.emsdk.3165-env
│ ├── Dockerfile.emsdk.406-env
│ ├── Dockerfile.linux-env
│ ├── Dockerfile.nssdk.1532-env
│ ├── Dockerfile.nssdk.1753-env
│ ├── Dockerfile.nssdk.1832-env
│ ├── Dockerfile.ps4.10500-env
│ ├── Dockerfile.ps4.11000-env
│ ├── Dockerfile.ps4.12000-env
│ ├── Dockerfile.ps5.10000-env
│ ├── Dockerfile.ps5.8000-env
│ ├── Dockerfile.ps5.9000-env
│ ├── Dockerfile.wine-env
│ ├── Dockerfile.winsdk.2019-env
│ ├── Dockerfile.winsdk.2022-env
│ ├── common-services.yml
│ ├── docker-compose.yml
│ └── winsdk_rename_files.py
├── envs
│ ├── .env
│ ├── generate_user_env.sh
│ └── macos.env
├── gradle.properties
├── manifestmergetool
│ ├── README.md
│ ├── build.gradle
│ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── defold
│ │ │ │ └── manifestmergetool
│ │ │ │ ├── AndroidManifestMerger.java
│ │ │ │ ├── HtmlMerger.java
│ │ │ │ ├── InfoPlistMerger.java
│ │ │ │ ├── ManifestMergeTool.java
│ │ │ │ ├── MergePolicy.java
│ │ │ │ └── PrivacyManifestMerger.java
│ │ └── resources
│ │ │ └── PropertyList-1.0.dtd
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── defold
│ │ └── manifestmergetool
│ │ └── ManifestMergeToolTest.java
├── scripts
│ ├── debug_defoldsdk.py
│ ├── standalone
│ │ ├── service-standalone.sh
│ │ └── setup-standalone-env.sh
│ ├── start-test-server.sh
│ └── stop-test-server.sh
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── defold
│ │ │ │ └── extender
│ │ │ │ ├── AppManifestConfiguration.java
│ │ │ │ ├── AppManifestPlatformConfig.java
│ │ │ │ ├── AsyncBuilder.java
│ │ │ │ ├── BuilderConstants.java
│ │ │ │ ├── Configuration.java
│ │ │ │ ├── Extender.java
│ │ │ │ ├── ExtenderApplication.java
│ │ │ │ ├── ExtenderController.java
│ │ │ │ ├── ExtenderException.java
│ │ │ │ ├── ExtenderUtil.java
│ │ │ │ ├── ExtenderYamlSafeConstructor.java
│ │ │ │ ├── ExtensionManifestValidator.java
│ │ │ │ ├── FrameworkUtil.java
│ │ │ │ ├── ManifestConfiguration.java
│ │ │ │ ├── ManifestPlatformConfig.java
│ │ │ │ ├── PlatformConfig.java
│ │ │ │ ├── TemplateExecutor.java
│ │ │ │ ├── Timer.java
│ │ │ │ ├── TreePrinter.java
│ │ │ │ ├── WebSecurityConfig.java
│ │ │ │ ├── WhitelistConfig.java
│ │ │ │ ├── ZipUtils.java
│ │ │ │ ├── builders
│ │ │ │ └── CSharpBuilder.java
│ │ │ │ ├── cache
│ │ │ │ ├── CacheEntry.java
│ │ │ │ ├── CacheKeyGenerator.java
│ │ │ │ ├── DataCache.java
│ │ │ │ ├── DataCacheException.java
│ │ │ │ ├── DataCacheFactory.java
│ │ │ │ ├── DummyDataCache.java
│ │ │ │ ├── GCPDataCache.java
│ │ │ │ ├── LocalDiskDataCache.java
│ │ │ │ └── info
│ │ │ │ │ ├── CacheInfoFileParser.java
│ │ │ │ │ ├── CacheInfoFileWriter.java
│ │ │ │ │ └── CacheInfoWrapper.java
│ │ │ │ ├── log
│ │ │ │ ├── ExtenderLogEnhancer.java
│ │ │ │ ├── ExtenderLogEnhancerConfiguration.java
│ │ │ │ └── Markers.java
│ │ │ │ ├── metrics
│ │ │ │ └── MetricsWriter.java
│ │ │ │ ├── process
│ │ │ │ ├── ProcessExecutor.java
│ │ │ │ └── ProcessUtils.java
│ │ │ │ ├── remote
│ │ │ │ ├── RemoteBuildException.java
│ │ │ │ ├── RemoteEngineBuilder.java
│ │ │ │ ├── RemoteHostConfiguration.java
│ │ │ │ └── RemoteInstanceConfig.java
│ │ │ │ ├── services
│ │ │ │ ├── DataCacheService.java
│ │ │ │ ├── DefoldSdkService.java
│ │ │ │ ├── DefoldSdkServiceConfiguration.java
│ │ │ │ ├── GCPInstanceService.java
│ │ │ │ ├── GradleService.java
│ │ │ │ ├── GradleServiceInterface.java
│ │ │ │ ├── HealthReporterService.java
│ │ │ │ ├── MockGradleService.java
│ │ │ │ ├── RealGradleService.java
│ │ │ │ ├── UserUpdateService.java
│ │ │ │ ├── cocoapods
│ │ │ │ │ ├── CocoaPodsService.java
│ │ │ │ │ ├── LanguageSet.java
│ │ │ │ │ ├── MainPodfile.java
│ │ │ │ │ ├── PlatformAndLanguageSet.java
│ │ │ │ │ ├── PlatformSet.java
│ │ │ │ │ ├── PlistBuddyWrapper.java
│ │ │ │ │ ├── PodSpec.java
│ │ │ │ │ ├── PodSpecParser.java
│ │ │ │ │ ├── PodUtils.java
│ │ │ │ │ └── ResolvedPods.java
│ │ │ │ └── data
│ │ │ │ │ ├── DefoldSdk.java
│ │ │ │ │ └── GCPInstanceState.java
│ │ │ │ └── tracing
│ │ │ │ ├── ExtenderExecutor.java
│ │ │ │ ├── ExtenderTracerInterceptor.java
│ │ │ │ └── TraceIdInResponseServletFilter.java
│ │ └── resources
│ │ │ ├── application.yml
│ │ │ ├── template.build.gradle
│ │ │ ├── template.csproj
│ │ │ ├── template.gradle.properties
│ │ │ ├── template.local.properties
│ │ │ ├── template.modulemap
│ │ │ ├── template.podfile
│ │ │ └── template.umbrella.h
│ └── test
│ │ ├── java
│ │ └── com
│ │ │ └── defold
│ │ │ └── extender
│ │ │ ├── AuthenticationTest.java
│ │ │ ├── ExtenderTest.java
│ │ │ ├── ExtenderUtilTest.java
│ │ │ ├── ExtensionManifestValidatorTest.java
│ │ │ ├── IntegrationTest.java
│ │ │ ├── TemplateExecutorTest.java
│ │ │ ├── TestUtils.java
│ │ │ ├── ZipUtilsTest.java
│ │ │ ├── cache
│ │ │ ├── CacheKeyGeneratorTest.java
│ │ │ ├── DummyDataCacheTest.java
│ │ │ ├── LocalDiskDataCacheTest.java
│ │ │ └── info
│ │ │ │ ├── CacheInfoFileParserTest.java
│ │ │ │ └── CacheInfoFileWriterTest.java
│ │ │ ├── remote
│ │ │ └── RemoteEngineBuilderTest.java
│ │ │ └── services
│ │ │ ├── DataCacheServiceTest.java
│ │ │ ├── DefoldSDKServiceTest.java
│ │ │ └── cocoapods
│ │ │ ├── CocoaPodsServiceTest.java
│ │ │ ├── PlistBuddyWrapperTest.java
│ │ │ └── PodUtilsTest.java
│ │ └── resources
│ │ ├── junit-platform.properties
│ │ └── upload
│ │ ├── dir
│ │ └── test1.txt
│ │ ├── dir2
│ │ └── test2.txt
│ │ ├── ne-cache-info.json
│ │ └── old-ne-cache-info.json
├── test-data
│ ├── AndroidManifest.xml
│ ├── _app
│ │ └── rjava
│ │ │ └── com
│ │ │ └── dummy
│ │ │ └── R.java
│ ├── alib
│ │ └── alib.cpp
│ ├── blib
│ │ ├── blib.cpp
│ │ └── blib.h
│ ├── build-libs.sh
│ ├── checksum_sdk
│ │ ├── create_sdk.sh
│ │ ├── defoldsdk
│ │ │ ├── sdk_file_1.yml
│ │ │ └── test_header.h
│ │ ├── test_sdk.sha256
│ │ ├── test_sdk.zip
│ │ └── test_sdk_invalid.sha256
│ ├── compile.sh
│ ├── createdebugsdk.sh
│ ├── dynamic_specific1
│ │ └── dynamic_specific1.cpp
│ ├── dynamic_specific2
│ │ └── dynamic_specific2.cpp
│ ├── enginelibs
│ │ ├── engine_foo.cpp
│ │ └── engine_main.cpp
│ ├── ext
│ │ ├── ext.manifest
│ │ ├── include
│ │ │ └── ext.h
│ │ ├── lib
│ │ │ ├── android
│ │ │ │ ├── Dummy.jar
│ │ │ │ ├── JarDep.jar
│ │ │ │ ├── VeryLarge1.jar
│ │ │ │ ├── VeryLarge2.jar
│ │ │ │ └── meta-inf.jar
│ │ │ ├── arm64-android
│ │ │ │ └── libalib.a
│ │ │ ├── arm64-ios
│ │ │ │ └── libalib.a
│ │ │ ├── arm64-osx
│ │ │ │ └── libalib.a
│ │ │ ├── armv7-android
│ │ │ │ └── libalib.a
│ │ │ ├── armv7-ios
│ │ │ │ └── libalib.a
│ │ │ ├── js-web
│ │ │ │ ├── libalib.a
│ │ │ │ └── library_dummy.js
│ │ │ ├── wasm-web
│ │ │ │ └── libalib.a
│ │ │ ├── x86-win32
│ │ │ │ └── alib.lib
│ │ │ ├── x86_64-linux
│ │ │ │ └── libalib.a
│ │ │ ├── x86_64-osx
│ │ │ │ ├── blib.framework
│ │ │ │ │ └── README.txt
│ │ │ │ └── libalib.a
│ │ │ └── x86_64-win32
│ │ │ │ ├── alib.lib
│ │ │ │ └── empty.lib
│ │ │ │ └── .gitkeep
│ │ └── src
│ │ │ ├── Test.java
│ │ │ ├── TestJar.java
│ │ │ └── test_ext.cpp
│ ├── ext2
│ │ ├── ext.manifest
│ │ ├── lib
│ │ │ ├── arm64-android
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── arm64-ios
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── arm64-osx
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── armv7-android
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── armv7-ios
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── js-web
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── wasm-web
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── x86-win32
│ │ │ │ ├── alib.lib
│ │ │ │ └── blib.lib
│ │ │ ├── x86_64-linux
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ ├── x86_64-osx
│ │ │ │ ├── libalib.a
│ │ │ │ └── libblib.a
│ │ │ └── x86_64-win32
│ │ │ │ ├── alib.lib
│ │ │ │ └── blib.lib
│ │ └── src
│ │ │ └── test_ext.cpp
│ ├── ext3
│ │ ├── ext.manifest
│ │ └── src
│ │ │ ├── README.md
│ │ │ └── extension.cpp
│ ├── ext_basic
│ │ ├── ext.manifest
│ │ ├── include
│ │ │ └── ext.h
│ │ └── src
│ │ │ └── test_ext.cpp
│ ├── ext_dyn_libs
│ │ ├── ext.manifest
│ │ ├── lib
│ │ │ ├── arm64-android
│ │ │ │ └── libdynamic_specific1.so
│ │ │ ├── arm64-ios
│ │ │ │ └── libdynamic_specific1.so
│ │ │ ├── arm64-osx
│ │ │ │ └── libdynamic_specific1.dylib
│ │ │ ├── armv7-android
│ │ │ │ └── libdynamic_specific1.so
│ │ │ ├── js-web
│ │ │ │ └── libdynamic_specific1.so
│ │ │ ├── wasm-web
│ │ │ │ └── libdynamic_specific1.so
│ │ │ ├── x86-win32
│ │ │ │ └── dynamic_specific1.dll
│ │ │ ├── x86_64-linux
│ │ │ │ └── libdynamic_specific1.so
│ │ │ ├── x86_64-osx
│ │ │ │ └── libdynamic_specific1.dylib
│ │ │ └── x86_64-win32
│ │ │ │ └── dynamic_specific1.dll
│ │ └── src
│ │ │ └── test_ext.cpp
│ ├── ext_dyn_libs2
│ │ ├── ext.manifest
│ │ ├── lib
│ │ │ ├── arm64-android
│ │ │ │ └── libdynamic_specific2.so
│ │ │ ├── arm64-ios
│ │ │ │ └── libdynamic_specific2.so
│ │ │ ├── arm64-osx
│ │ │ │ └── libdynamic_specific2.dylib
│ │ │ ├── armv7-android
│ │ │ │ └── libdynamic_specific2.so
│ │ │ ├── js-web
│ │ │ │ └── libdynamic_specific2.so
│ │ │ ├── wasm-web
│ │ │ │ └── libdynamic_specific2.so
│ │ │ ├── x86-win32
│ │ │ │ └── dynamic_specific2.dll
│ │ │ ├── x86_64-linux
│ │ │ │ └── libdynamic_specific2.so
│ │ │ ├── x86_64-osx
│ │ │ │ └── libdynamic_specific2.dylib
│ │ │ └── x86_64-win32
│ │ │ │ └── dynamic_specific2.dll
│ │ └── src
│ │ │ └── extension.cpp
│ ├── ext_error_extension
│ │ ├── ext.manifest
│ │ └── src
│ │ │ └── test_error_ext.cpp
│ ├── ext_std
│ │ ├── ext.manifest
│ │ ├── include
│ │ │ └── std.h
│ │ ├── lib
│ │ │ ├── arm64-android
│ │ │ │ └── libstd.a
│ │ │ ├── arm64-ios
│ │ │ │ └── libstd.a
│ │ │ ├── arm64-osx
│ │ │ │ └── libstd.a
│ │ │ ├── armv7-android
│ │ │ │ └── libstd.a
│ │ │ ├── armv7-ios
│ │ │ │ └── libstd.a
│ │ │ ├── js-web
│ │ │ │ └── libstd.a
│ │ │ ├── wasm-web
│ │ │ │ └── libstd.a
│ │ │ ├── x86-win32
│ │ │ │ └── std.lib
│ │ │ ├── x86_64-linux
│ │ │ │ └── libstd.a
│ │ │ ├── x86_64-osx
│ │ │ │ └── libstd.a
│ │ │ └── x86_64-win32
│ │ │ │ └── std.lib
│ │ └── src
│ │ │ └── test_ext.cpp
│ ├── ext_use_base_extension
│ │ ├── ext.manifest
│ │ └── src
│ │ │ └── test_ext.cpp
│ ├── extendertest.appmanifest
│ ├── extendertest.emptycontext.manifest
│ ├── extendertest.platformnull.appmanifest
│ ├── headless.appmanifest
│ ├── invalidfilenames
│ │ ├── ext.manifest
│ │ ├── include
│ │ │ └── foo;echo hello;.h
│ │ └── src
│ │ │ └── test.cpp
│ ├── jars
│ │ ├── META-INF
│ │ │ └── inner.folder
│ │ │ │ ├── com.inner
│ │ │ │ ├── inner.pom.properties
│ │ │ │ ├── inner.pom.xml
│ │ │ │ └── io.foo.service.HTTPClient
│ │ ├── build.sh
│ │ ├── com
│ │ │ └── defold
│ │ │ │ └── dummy
│ │ │ │ └── Dummy.java
│ │ └── io
│ │ │ └── foo
│ │ │ └── dummy
│ │ │ └── FooDummy.java
│ ├── manifest_override
│ │ └── upload
│ │ │ ├── _app
│ │ │ └── app.manifest
│ │ │ └── extension1
│ │ │ └── ext.manifest
│ ├── pod_specs
│ │ ├── AXPracticalHUD.json
│ │ ├── Cuckoo.json
│ │ ├── PNChartboostSDKAdapter.json
│ │ ├── PubNub.json
│ │ ├── Rtc555Sdk.json
│ │ ├── TPNiOS.json
│ │ ├── UnityAds.json
│ │ ├── Wilddog.json
│ │ └── streethawk.json
│ ├── podfiles
│ │ ├── empty.Podfile
│ │ ├── regular.Podfile
│ │ ├── with_comments.Podfile
│ │ └── wrong.Podfile
│ ├── query1
│ │ ├── a.txt
│ │ ├── b.txt
│ │ └── ne-cache-info.json
│ ├── sdk
│ │ └── a
│ │ │ └── defoldsdk
│ │ │ ├── extender
│ │ │ ├── build.yml
│ │ │ └── variants
│ │ │ │ ├── debug.appmanifest
│ │ │ │ ├── headless.appmanifest
│ │ │ │ └── release.appmanifest
│ │ │ ├── lib
│ │ │ ├── arm64-android
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── arm64-ios
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── arm64-osx
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── armv7-android
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── armv7-ios
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── darwin
│ │ │ │ ├── libengine_foo.a
│ │ │ │ ├── libengine_main.a
│ │ │ │ └── x86-osx
│ │ │ │ │ ├── libengine_foo.a
│ │ │ │ │ └── libengine_main.a
│ │ │ ├── js-web
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── linux
│ │ │ │ ├── libengine_foo.a
│ │ │ │ ├── libengine_main.a
│ │ │ │ └── x86-linux
│ │ │ │ │ ├── libengine_foo.a
│ │ │ │ │ └── libengine_main.a
│ │ │ ├── wasm-web
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── x86-win32
│ │ │ │ ├── OpenAL32.lib
│ │ │ │ ├── defold.ico
│ │ │ │ ├── engine.rc
│ │ │ │ ├── engine_foo.lib
│ │ │ │ └── engine_main.lib
│ │ │ ├── x86_64-darwin
│ │ │ │ ├── libengine_foo.a
│ │ │ │ ├── libengine_main.a
│ │ │ │ └── x86_64-osx
│ │ │ │ │ ├── libengine_foo.a
│ │ │ │ │ └── libengine_main.a
│ │ │ ├── x86_64-linux
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ ├── x86_64-macos
│ │ │ │ ├── libengine_foo.a
│ │ │ │ ├── libengine_main.a
│ │ │ │ └── x86_64-osx
│ │ │ │ │ ├── libengine_foo.a
│ │ │ │ │ └── libengine_main.a
│ │ │ ├── x86_64-osx
│ │ │ │ ├── libengine_foo.a
│ │ │ │ └── libengine_main.a
│ │ │ └── x86_64-win32
│ │ │ │ ├── OpenAL32.lib
│ │ │ │ ├── defold.ico
│ │ │ │ ├── engine.rc
│ │ │ │ ├── engine_foo.lib
│ │ │ │ └── engine_main.lib
│ │ │ └── share
│ │ │ ├── java
│ │ │ ├── Engine.jar
│ │ │ ├── build.sh
│ │ │ └── com
│ │ │ │ └── defoldtest
│ │ │ │ └── engine
│ │ │ │ └── Engine.java
│ │ │ ├── js-web-pre-engine.js
│ │ │ └── js-web-pre.js
│ ├── stdlib
│ │ ├── std.cpp
│ │ └── std.h
│ ├── test_query.sh
│ ├── testproject
│ │ ├── a
│ │ │ └── notincluded.h
│ │ └── b
│ │ │ ├── ext.manifest
│ │ │ ├── include
│ │ │ └── b.h
│ │ │ ├── lib
│ │ │ ├── arm64-osx
│ │ │ │ └── x86-osx.a
│ │ │ ├── ios
│ │ │ │ └── ios.a
│ │ │ └── osx
│ │ │ │ └── osx.a
│ │ │ └── src
│ │ │ └── b.cpp
│ ├── testproject_appmanifest
│ │ ├── README.txt
│ │ ├── _app
│ │ │ └── app.manifest
│ │ ├── build.sh
│ │ ├── ext
│ │ │ ├── ext.manifest
│ │ │ ├── lib
│ │ │ │ ├── arm64-android
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── arm64-ios
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── arm64-osx
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── armv7-android
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── armv7-ios
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── js-web
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── wasm-web
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── x86-linux
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── x86-win32
│ │ │ │ │ └── alib.lib
│ │ │ │ ├── x86_64-linux
│ │ │ │ │ └── libalib.a
│ │ │ │ ├── x86_64-osx
│ │ │ │ │ └── libalib.a
│ │ │ │ └── x86_64-win32
│ │ │ │ │ └── alib.lib
│ │ │ └── src
│ │ │ │ └── test_ext.cpp
│ │ ├── ext2
│ │ │ ├── ext.manifest
│ │ │ ├── lib
│ │ │ │ ├── android
│ │ │ │ │ ├── Dummy1.jar
│ │ │ │ │ └── Dummy2.jar
│ │ │ │ ├── arm64-android
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── arm64-ios
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── arm64-osx
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── armv7-android
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── armv7-ios
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── js-web
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── wasm-web
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── x86-linux
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── x86-win32
│ │ │ │ │ └── blib.lib
│ │ │ │ ├── x86_64-linux
│ │ │ │ │ └── libblib.a
│ │ │ │ ├── x86_64-osx
│ │ │ │ │ └── libblib.a
│ │ │ │ └── x86_64-win32
│ │ │ │ │ └── blib.lib
│ │ │ └── src
│ │ │ │ └── test_ext.cpp
│ │ └── libsource
│ │ │ ├── a.cpp
│ │ │ └── b.cpp
│ └── testproject_appmanifest_variant
│ │ └── _app
│ │ └── app.manifest
└── users
│ ├── testusers.txt
│ └── users.txt
└── settings.gradle
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "github-actions"
9 | directory: "/"
10 | schedule:
11 | interval: "weekly"
12 | - package-ecosystem: "gradle"
13 | directories:
14 | - "/"
15 | schedule:
16 | interval: "weekly"
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/build
2 | **/classes
3 | /client/.buildcache
4 | /tmp
5 | /debugsdk
6 | /defoldsdk
7 | /localserver
8 | /platformsdk
9 | *.class
10 | !server/test-data/sdk/
11 | server/test-data/sdk/*
12 | !server/test-data/sdk/a
13 |
14 | # C++
15 | /**/*.o
16 | /**/*.pdb
17 |
18 | # IntelliJ IDEA
19 | .idea/*
20 | !/.idea/misc.xml
21 | !/.idea/modules.xml
22 | !/.idea/vcs.xml
23 | !/.idea/gradle.xml
24 | !/.idea/runConfigurations/
25 | !/.idea/modules/
26 | !/.idea/modules/extender.iml
27 | .idea/workspace.xml
28 | .idea/assetWizardSettings.xml
29 | .idea/dictionaries/
30 | .idea/shelf/
31 | .idea/libraries/
32 | .idea/httpRequests/
33 | /*.iml
34 | /*.ipr
35 | /*.iws
36 | /server/out
37 |
38 | # Eclipse
39 | /.classpath
40 | /.project
41 | /.settings
42 |
43 | # Gradle
44 | **/.gradle
45 |
46 | # Mac
47 | .DS_Store
48 | .bash_history
49 |
50 | Version.java
51 | /server/app/*
52 | server/bin/*
53 | server/manifestmergetool/app/*
54 | server/manifestmergetool/bin/*
55 |
56 | /server/docker/victoria-metrics-data/*
57 | /server/docker/grafana-data/*
58 |
59 | /server/envs/user.env
60 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/defold/extender/499c3f1381958dc137038d313946a07e0c73c563/.gitmodules
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | IDE
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/modules/client/extender.client.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/client/extender.client.test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules/server/extender.server.main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Attach_to_java_process_inside_docker.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Run_standalone_Extender.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "vmware.vscode-boot-dev-pack",
4 | "vscjava.vscode-spring-boot-dashboard",
5 | "redhat.java", "vscjava.vscode-gradle",
6 | "vscjava.vscode-java-debug",
7 | ]
8 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "type": "java",
5 | "name": "Attach to java process inside docker",
6 | "request": "attach",
7 | "hostName": "localhost",
8 | "port": "5005"
9 | },
10 | {
11 | "type": "java",
12 | "name": "Spring Boot-ExtenderApplication",
13 | "request": "launch",
14 | "cwd": "${workspaceFolder}",
15 | "vmArgs": "-Dorg.eclipse.jetty.server.Request.maxFormKeys=1500",
16 | "mainClass": "com.defold.extender.ExtenderApplication",
17 | "projectName": "server",
18 | "args": "--spring.config.location=classpath:./,file:${workspaceFolder}/server/configs/ --extender.sdk.location=${workspaceFolder}/server/app/sdk --spring.profiles.active=standalone-dev",
19 | "envFile": ["${workspaceFolder}/server/envs/.env", "${workspaceFolder}/server/envs/user.env", "${workspaceFolder}/server/envs/macos.env"]
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.compile.nullAnalysis.mode": "automatic",
3 | "java.configuration.updateBuildConfiguration": "automatic"
4 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Defold Foundation
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README_CLIENT.md:
--------------------------------------------------------------------------------
1 |
2 | # Extender Client
3 |
4 | There is a client part of the Extender code which is used in Bob.jar.
5 |
6 | 1. Build the client
7 |
8 | $ cd client
9 | $ ../gradlew build
10 |
11 | 1. Copy the client to Bob
12 |
13 | $ cp -v ./build/libs/extender-client-0.0.1.jar /com.dynamo.cr/com.dynamo.cr.common/ext/extender-client-0.0.1.jar
14 |
15 |
16 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | subprojects {
2 | repositories {
3 | mavenCentral()
4 | }
5 | }
6 |
7 | if (project.hasProperty('publishToGar')) {
8 | apply plugin: 'maven-publish'
9 | apply plugin: 'com.google.cloud.artifactregistry.gradle-plugin'
10 | publishing {
11 | repositories {
12 | maven {
13 | name "extenderRepo"
14 | url "artifactregistry://europe-west1-maven.pkg.dev/extender-426409/extender-maven/"
15 | }
16 | }
17 | }
18 | }
19 |
20 | wrapper {
21 | gradleVersion = '8.9'
22 | distributionType = Wrapper.DistributionType.ALL
23 | }
24 |
--------------------------------------------------------------------------------
/client/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | }
4 |
5 | sourceCompatibility = 1.8
6 |
7 | repositories {
8 | mavenCentral()
9 | }
10 |
11 | ext {
12 | clientVersion = findProperty('clientVersion') ?: '0.5.0'
13 | }
14 |
15 | if (project.hasProperty('publishToGar')) {
16 | apply plugin: 'com.google.cloud.artifactregistry.gradle-plugin'
17 | publishing {
18 | publications {
19 | extenderClient(MavenPublication) {
20 | artifact jar
21 | groupId 'com.defold.extender'
22 | artifactId 'client'
23 | version project.ext.clientVersion
24 | }
25 | }
26 | }
27 | }
28 |
29 | jar {
30 | archiveBaseName = 'extender-client'
31 | version = project.ext.clientVersion
32 | }
33 |
34 | dependencies {
35 | implementation('org.apache.httpcomponents:httpclient:4.5.14')
36 | implementation('org.apache.httpcomponents:httpmime:4.5.14')
37 | implementation('com.googlecode.json-simple:json-simple:1.1.1') {
38 | exclude module: 'junit'
39 | }
40 | testImplementation('commons-io:commons-io:2.18.0')
41 | testImplementation("org.mockito:mockito-core:5.+")
42 | testImplementation('org.junit.jupiter:junit-jupiter-engine:5.11.4')
43 | }
44 |
45 | test {
46 | useJUnitPlatform()
47 | }
--------------------------------------------------------------------------------
/client/scripts/build_and_copy.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 |
3 | (cd client && ../gradlew build && cp -v ./build/libs/extender-client-0.0.5.jar $DYNAMO_HOME/../../com.dynamo.cr/com.dynamo.cr.common/ext/extender-client-0.0.5.jar)
4 |
--------------------------------------------------------------------------------
/client/src/main/java/com/defold/extender/client/ExtenderClientException.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.client;
2 |
3 | public class ExtenderClientException extends Exception {
4 |
5 | public ExtenderClientException(String message) {
6 | super(message);
7 | }
8 |
9 | public ExtenderClientException(String message, Throwable cause) {
10 | super(message, cause);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/client/src/main/java/com/defold/extender/client/ExtenderResource.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.client;
2 |
3 | import java.io.IOException;
4 |
5 | public interface ExtenderResource {
6 |
7 | String getPath();
8 |
9 | byte[] getContent() throws IOException;
10 |
11 | long getLastModified();
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/client/src/main/java/com/defold/extender/client/FileExtenderResource.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.client;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.nio.file.Files;
6 |
7 | public class FileExtenderResource implements ExtenderResource {
8 | private File file = null;
9 | private String filePath;
10 |
11 | public FileExtenderResource(String filePath) {
12 | this(new File(filePath));
13 | }
14 |
15 | public FileExtenderResource(String filePath, String zipPath) {
16 | this(new File(filePath));
17 | this.filePath = zipPath;
18 | }
19 |
20 | FileExtenderResource(File file) {
21 | this.file = file;
22 | this.filePath = file.getPath();
23 | }
24 |
25 | @Override
26 | public String getPath() {
27 | return filePath;
28 | }
29 |
30 | @Override
31 | public byte[] getContent() throws IOException {
32 | return Files.readAllBytes(this.file.toPath());
33 | }
34 |
35 | @Override
36 | public long getLastModified() {
37 | return file.lastModified();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/defold/extender/499c3f1381958dc137038d313946a07e0c73c563/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/server/configs/application-dev.yml:
--------------------------------------------------------------------------------
1 | extender:
2 | cache:
3 | enabled: true
4 | type: LOCAL
5 | local.basedir: /var/extender/cache/data
6 |
--------------------------------------------------------------------------------
/server/configs/application-influx.yml:
--------------------------------------------------------------------------------
1 | management:
2 | metrics:
3 | tags:
4 | application: extender
5 | influx:
6 | metrics:
7 | export:
8 | api-version: v2 # API version of InfluxDB to use. Defaults to 'v1' unless an org is configured. If an org is configured, defaults to 'v2'.
9 | auto-create-db: true # Whether to create the InfluxDB database if it does not exist before attempting to publish metrics to it. InfluxDB v1 only. (Default: true)
10 | batch-size: 10000 # Number of measurements per request to use for this backend. If more measurements are found, then multiple requests will be made. (Default: 10000)
11 | bucket: metrics # Bucket for metrics. Use either the bucket name or ID. Defaults to the value of the db property if not set. InfluxDB v2 only.
12 | compressed: true # Whether to enable GZIP compression of metrics batches published to InfluxDB. (Default: true)
13 | connect-timeout: 1s # Connection timeout for requests to this backend. (Default: 1s)
14 | consistency: one # Write consistency for each point. (Default: one)
15 | # db: extender # Database to send metrics to. InfluxDB v1 only. (Default: mydb)
16 | enabled: true # Whether exporting of metrics to this backend is enabled. (Default: true)
17 | num-threads: 2 # Number of threads to use with the metrics publishing scheduler. (Default: 2)
18 | org: defold # Org to write metrics to. InfluxDB v2 only.
19 | # password: mysecret # Login password of the InfluxDB server. InfluxDB v1 only.
20 | read-timeout: 10s # Read timeout for requests to this backend. (Default: 10s)
21 | # retention-policy: my_rp # Retention policy to use (InfluxDB writes to the DEFAULT retention policy if one is not specified). InfluxDB v1 only.
22 | step: 1m # Step size (i.e. reporting frequency) to use. (Default: 1m)
23 | token: "foo" # Authentication token to use with calls to the InfluxDB backend. For InfluxDB v1, the Bearer scheme is used. For v2, the Token scheme is used.
24 | uri: http://victoriametrics:8428 # URI of the InfluxDB server. (Default: http://localhost:8086)
25 | # user-name: myusername # Login user of the InfluxDB server. InfluxDB v1 only.
26 |
--------------------------------------------------------------------------------
/server/configs/application-local-dev-android.yml:
--------------------------------------------------------------------------------
1 | extender.gradle.enabled: true
2 |
--------------------------------------------------------------------------------
/server/configs/application-local-dev-app.yml:
--------------------------------------------------------------------------------
1 | extender:
2 | remote-builder:
3 | enabled: true
4 | platforms:
5 | android-ndk25:
6 | url: http://android-ndk25:9000
7 | instanceId: android-ndk25
8 | alwaysOn: true
9 | emsdk-3165:
10 | url: http://emsdk-3165:9000
11 | instanceId: emsdk-3165
12 | emsdk-406:
13 | url: http://emsdk-406:9000
14 | instanceId: emsdk-406
15 | linux-latest:
16 | url: http://linux:9000
17 | instanceId: linux
18 | linux-arm64-latest:
19 | url: http://linux-arm64:9000
20 | instanceId: linux-arm64
21 | nssdk-1832:
22 | url: http://nssdk-1832:9000
23 | instanceId: nssdk-1832
24 | ps4-12000:
25 | url: http://ps4-12000:9000
26 | instanceId: ps4-12000
27 | ps5-10000:
28 | url: http://ps5-10000:9000
29 | instanceId: ps5-10000
30 | winsdk-2022:
31 | url: http://winsdk-2022:9000
32 | instanceId: winsdk-2022
33 |
34 | logging.pattern.level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
35 |
--------------------------------------------------------------------------------
/server/configs/application-local-dev.yml:
--------------------------------------------------------------------------------
1 | extender:
2 | # sdk.location: /usr/local/extender/sdk
3 | cache:
4 | enabled: true
5 | local.basedir: /var/extender/cache/data
6 | type: LOCAL
7 |
8 | job-result:
9 | location: /var/extender/results
10 | cleanup-period: 20000
11 |
12 | logging.pattern.level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
--------------------------------------------------------------------------------
/server/configs/application-logging.yml:
--------------------------------------------------------------------------------
1 | spring.main.banner-mode: "off"
2 | logging.config: /etc/defold/extender/extender-logging.xml
3 | logging.pattern.level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
4 | management.tracing.sampling.probability: 1.0
--------------------------------------------------------------------------------
/server/configs/application-metrics.yml:
--------------------------------------------------------------------------------
1 | management.metrics.tags.instance: ${INSTANCE_ID}
--------------------------------------------------------------------------------
/server/configs/application-standalone-dev.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9010
3 |
4 | extender:
5 | sdk:
6 | location: /usr/local/extender/sdk
7 | cache-clear-on-exit: false
8 | cache:
9 | enabled: false
10 | remote-builder:
11 | enabled: false
12 | cocoapods:
13 | enabled: true
14 | home-dir-prefix: /tmp/extender/.cocoapods
15 | repo-update-cron: "0 0 * * * *" # update spec repo every 1 h
16 | cache-dir-rotate-cron: "0 10 2 * * *" # once per day
17 | old-cache-clean-cron: "0 10 6 * * *" # once per day after directory rotation
18 |
--------------------------------------------------------------------------------
/server/configs/application-test-app.yml:
--------------------------------------------------------------------------------
1 | extender:
2 | remote-builder:
3 | enabled: true
4 | platforms:
5 | android-ndk25:
6 | url: http://android-ndk25:9000
7 | instanceId: android-ndk25
8 | alwaysOn: true
9 | emsdk-3155:
10 | url: http://emsdk-3155:9000
11 | instanceId: emsdk-3155
12 | emsdk-3165:
13 | url: http://emsdk-3165:9000
14 | instanceId: emsdk-3165
15 | linux-latest:
16 | url: http://linux:9000
17 | instanceId: linux
18 | linux-arm64-latest:
19 | url: http://linux-arm64:9000
20 | instanceId: linux-arm64
21 | nssdk-1832:
22 | url: http://nssdk-1832:9000
23 | instanceId: nssdk-1832
24 | ps4-12000:
25 | url: http://ps4-12000:9000
26 | instanceId: ps4-12000
27 | ps5-10000:
28 | url: http://ps5-10000:9000
29 | instanceId: ps5-10000
30 | winsdk-2022:
31 | url: http://winsdk-2022:9000
32 | instanceId: winsdk-2022
33 |
34 | logging.pattern.level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
35 |
--------------------------------------------------------------------------------
/server/configs/extender-logging.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/server/docker/Dockerfile.android-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:1.4.0
2 |
3 | # Technically part of the Android build, but also required by the GradleService.java
4 | # until it can be disabled (e.g. by looking for the existance of GRADLE_USER_HOME)
5 |
6 | # Versions: https://developer.android.com/studio/releases/gradle-plugin?buildsystem=ndk-build#updating-gradle
7 | # Java+Gradle version matrix: https://docs.gradle.org/current/userguide/compatibility.html
8 | ENV GRADLE_USER_HOME=/tmp/.gradle
9 | ENV GRADLE_VERSION=8.7
10 | ENV GRADLE_PLUGIN_VERSION=8.6.1
11 | ENV PATH=${PATH}:/opt/gradle/gradle-${GRADLE_VERSION}/bin
12 | RUN \
13 | echo "Gradle" && \
14 | wget -q https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip && \
15 | mkdir /opt/gradle && \
16 | unzip -q -d /opt/gradle gradle-${GRADLE_VERSION}-bin.zip && \
17 | rm gradle-${GRADLE_VERSION}-bin.zip && \
18 | which gradle && \
19 | chown -R extender: /opt/gradle
20 |
--------------------------------------------------------------------------------
/server/docker/Dockerfile.emsdk.2011-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:1.4.0
2 |
3 | #
4 | # EMSCRIPTEN
5 | #
6 |
7 | ENV EMCC_SKIP_SANITY_CHECK=1
8 |
9 | ## Emscripten 2.0.11 (from version 1.2.178)
10 |
11 | ENV EMSCRIPTEN_SDK_2_0_11=${PLATFORMSDK_DIR}/emsdk-2.0.11
12 | ENV EMSCRIPTEN_HOME_2_0_11=${EMSCRIPTEN_SDK_2_0_11}
13 | ENV EMSCRIPTEN_CONFIG_2_0_11=${EMSCRIPTEN_HOME_2_0_11}/.emscripten
14 | ENV EMSCRIPTEN_BIN_2_0_11=${EMSCRIPTEN_HOME_2_0_11}/upstream/emscripten
15 | ENV EMSCRIPTEN_CACHE_2_0_11=/var/extender/emcache_2_0_11
16 | ENV EMSCRIPTEN_PYTHON_2_0_11=/usr/bin/python3.10
17 | # Setup a special env variable that will be prefixed to PATH if requested version is 2.0.11
18 | ENV EMSCRIPTEN_PATH_2_0_11=${EMSCRIPTEN_HOME_2_0_11}:${EMSCRIPTEN_HOME_2_0_11}/upstream/bin:${EMSCRIPTEN_HOME_2_0_11}/node/12.9.1_64bit/bin:${EMSCRIPTEN_BIN_2_0_11}
19 |
20 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
21 | mkdir ${EMSCRIPTEN_SDK_2_0_11} && \
22 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/emsdk-2.0.11-linux.tar.gz | tar xz -C ${EMSCRIPTEN_SDK_2_0_11} --strip-components=1
23 |
24 | RUN \
25 | ${EMSCRIPTEN_HOME_2_0_11}/emsdk activate sdk-2.0.11-64bit --embedded && \
26 | EM_CONFIG=$EMSCRIPTEN_CONFIG_2_0_11 EM_CACHE=${EMSCRIPTEN_CACHE_2_0_11} python3 ${EMSCRIPTEN_BIN_2_0_11}/embuilder.py build SYSTEM MINIMAL && \
27 | chmod -R 755 ${EMSCRIPTEN_HOME_2_0_11} && \
28 | chown -R extender: ${EMSCRIPTEN_CACHE_2_0_11} && \
29 | chown -R extender: ${EMSCRIPTEN_CACHE_2_0_11}/wasm/cache.lock
30 |
31 | # We use the same temp directory for both versions.
32 | ENV EMSCRIPTEN_TEMP_DIR=/var/extender/ems_temp
33 | RUN mkdir -p ${EMSCRIPTEN_TEMP_DIR}
34 | RUN chmod -R 755 ${EMSCRIPTEN_TEMP_DIR} && chown extender: ${EMSCRIPTEN_TEMP_DIR}
35 | # The "sed" command below removes the /TEMP_DIR line from the generated configs
36 | # We replace it with a folder of our own
37 | RUN sed '/TEMP_DIR =/d' ${EMSCRIPTEN_CONFIG_2_0_11} && \
38 | echo TEMP_DIR = \'${EMSCRIPTEN_TEMP_DIR}\' >> ${EMSCRIPTEN_CONFIG_2_0_11}
--------------------------------------------------------------------------------
/server/docker/Dockerfile.emsdk.3155-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:1.4.0
2 |
3 | ENV EMSCRIPTEN_SDK_3_1_55=${PLATFORMSDK_DIR}/emsdk-3.1.55
4 | ENV EMSCRIPTEN_HOME_3_1_55=${EMSCRIPTEN_SDK_3_1_55}
5 | ENV EMSCRIPTEN_CACHE_3_1_55=/var/extender/emcache_3_1_55
6 | ENV EMSCRIPTEN_CONFIG_3_1_55=${EMSCRIPTEN_HOME_3_1_55}/.emscripten
7 | ENV EMSCRIPTEN_PYTHON_3_1_55=/usr/bin/python3.10
8 | ENV EMSCRIPTEN_BIN_3_1_55=${EMSCRIPTEN_HOME_3_1_55}/upstream/emscripten
9 | ENV EMSCRIPTEN_PATH_3_1_55=${EMSCRIPTEN_HOME_3_1_55}:${EMSCRIPTEN_HOME_3_1_55}/upstream/bin:${EMSCRIPTEN_HOME_3_1_55}/node/16.20.0_64bit/bin:${EMSCRIPTEN_BIN_3_1_55}
10 |
11 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
12 | mkdir ${EMSCRIPTEN_SDK_3_1_55} && \
13 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/emsdk-3.1.55-x86_64-linux.tar.gz | tar xz -C ${EMSCRIPTEN_SDK_3_1_55} --strip-components=1
14 |
15 | RUN \
16 | ${EMSCRIPTEN_HOME_3_1_55}/emsdk activate sdk-3.1.55-64bit && \
17 | EM_CONFIG=$EMSCRIPTEN_CONFIG_3_1_55 EM_CACHE=${EMSCRIPTEN_CACHE_3_1_55} python3 ${EMSCRIPTEN_BIN_3_1_55}/embuilder.py build SYSTEM MINIMAL && \
18 | chmod -R 755 ${EMSCRIPTEN_HOME_3_1_55} && \
19 | chown -R extender: ${EMSCRIPTEN_CACHE_3_1_55}
20 |
21 |
22 | # We use the same temp directory for both versions.
23 | ENV EMSCRIPTEN_TEMP_DIR=/var/extender/ems_temp
24 | RUN mkdir -p ${EMSCRIPTEN_TEMP_DIR}
25 | RUN chmod -R 755 ${EMSCRIPTEN_TEMP_DIR} && chown extender: ${EMSCRIPTEN_TEMP_DIR}
26 | # The "sed" command below removes the /TEMP_DIR line from the generated configs
27 | # We replace it with a folder of our own
28 | RUN sed '/TEMP_DIR =/d' ${EMSCRIPTEN_CONFIG_3_1_55} && \
29 | echo TEMP_DIR = \'${EMSCRIPTEN_TEMP_DIR}\' >> ${EMSCRIPTEN_CONFIG_3_1_55}
30 |
--------------------------------------------------------------------------------
/server/docker/Dockerfile.emsdk.3165-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:1.4.0
2 |
3 | ENV EMSCRIPTEN_SDK_3_1_65=${PLATFORMSDK_DIR}/emsdk-3.1.65
4 | ENV EMSCRIPTEN_HOME_3_1_65=${EMSCRIPTEN_SDK_3_1_65}
5 | ENV EMSCRIPTEN_CACHE_3_1_65=/var/extender/emcache_3_1_65
6 | ENV EMSCRIPTEN_CONFIG_3_1_65=${EMSCRIPTEN_HOME_3_1_65}/.emscripten
7 | ENV EMSCRIPTEN_PYTHON_3_1_65=/usr/bin/python3.10
8 | ENV EMSCRIPTEN_BIN_3_1_65=${EMSCRIPTEN_HOME_3_1_65}/upstream/emscripten
9 | ENV EMSCRIPTEN_PATH_3_1_65=${EMSCRIPTEN_HOME_3_1_65}:${EMSCRIPTEN_HOME_3_1_65}/upstream/bin:${EMSCRIPTEN_HOME_3_1_65}/node/16.20.0_64bit/bin:${EMSCRIPTEN_BIN_3_1_65}
10 |
11 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
12 | mkdir ${EMSCRIPTEN_SDK_3_1_65} && \
13 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/emsdk-3.1.65-x86_64-linux.tar.gz | tar xz -C ${EMSCRIPTEN_SDK_3_1_65} --strip-components=1
14 |
15 | RUN \
16 | ${EMSCRIPTEN_HOME_3_1_65}/emsdk activate sdk-3.1.65-64bit && \
17 | EM_CONFIG=$EMSCRIPTEN_CONFIG_3_1_65 EM_CACHE=${EMSCRIPTEN_CACHE_3_1_65} python3 ${EMSCRIPTEN_BIN_3_1_65}/embuilder.py build SYSTEM MINIMAL && \
18 | chmod -R 755 ${EMSCRIPTEN_HOME_3_1_65} && \
19 | chown -R extender: ${EMSCRIPTEN_CACHE_3_1_65}
20 |
21 |
22 | # We use the same temp directory for both versions.
23 | ENV EMSCRIPTEN_TEMP_DIR=/var/extender/ems_temp
24 | RUN mkdir -p ${EMSCRIPTEN_TEMP_DIR}
25 | RUN chmod -R 755 ${EMSCRIPTEN_TEMP_DIR} && chown extender: ${EMSCRIPTEN_TEMP_DIR}
26 | # The "sed" command below removes the /TEMP_DIR line from the generated configs
27 | # We replace it with a folder of our own
28 | RUN sed '/TEMP_DIR =/d' ${EMSCRIPTEN_CONFIG_3_1_65} && \
29 | echo TEMP_DIR = \'${EMSCRIPTEN_TEMP_DIR}\' >> ${EMSCRIPTEN_CONFIG_3_1_65}
30 |
--------------------------------------------------------------------------------
/server/docker/Dockerfile.emsdk.406-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:1.4.0
2 |
3 | ENV EMSCRIPTEN_SDK_4_0_6=${PLATFORMSDK_DIR}/emsdk-4.0.6
4 | ENV EMSCRIPTEN_HOME_4_0_6=${EMSCRIPTEN_SDK_4_0_6}
5 | ENV EMSCRIPTEN_CACHE_4_0_6=/var/extender/emcache_4_0_6
6 | ENV EMSCRIPTEN_CONFIG_4_0_6=${EMSCRIPTEN_HOME_4_0_6}/.emscripten
7 | ENV EMSCRIPTEN_PYTHON_4_0_6=/usr/bin/python3.10
8 | ENV EMSCRIPTEN_BIN_4_0_6=${EMSCRIPTEN_HOME_4_0_6}/upstream/emscripten
9 | ENV EMSCRIPTEN_PATH_4_0_6=${EMSCRIPTEN_HOME_4_0_6}:${EMSCRIPTEN_HOME_4_0_6}/upstream/bin:${EMSCRIPTEN_HOME_4_0_6}/node/20.18.0_64bit/bin:${EMSCRIPTEN_BIN_4_0_6}
10 |
11 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
12 | mkdir ${EMSCRIPTEN_SDK_4_0_6} && \
13 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/emsdk-4.0.6-x86_64-linux.tar.gz | tar xz -C ${EMSCRIPTEN_SDK_4_0_6} --strip-components=1
14 |
15 | RUN \
16 | ${EMSCRIPTEN_HOME_4_0_6}/emsdk activate sdk-4.0.6-64bit && \
17 | EM_CONFIG=$EMSCRIPTEN_CONFIG_4_0_6 EM_CACHE=${EMSCRIPTEN_CACHE_4_0_6} python3 ${EMSCRIPTEN_BIN_4_0_6}/embuilder.py build SYSTEM MINIMAL && \
18 | chmod -R 755 ${EMSCRIPTEN_HOME_4_0_6} && \
19 | chown -R extender: ${EMSCRIPTEN_CACHE_4_0_6}
20 |
21 |
22 | # We use the same temp directory for both versions.
23 | ENV EMSCRIPTEN_TEMP_DIR=/var/extender/ems_temp
24 | RUN mkdir -p ${EMSCRIPTEN_TEMP_DIR}
25 | RUN chmod -R 755 ${EMSCRIPTEN_TEMP_DIR} && chown extender: ${EMSCRIPTEN_TEMP_DIR}
26 | # The "sed" command below removes the /TEMP_DIR line from the generated configs
27 | # We replace it with a folder of our own
28 | RUN sed '/TEMP_DIR =/d' ${EMSCRIPTEN_CONFIG_4_0_6} && \
29 | echo TEMP_DIR = \'${EMSCRIPTEN_TEMP_DIR}\' >> ${EMSCRIPTEN_CONFIG_4_0_6}
30 |
--------------------------------------------------------------------------------
/server/docker/Dockerfile.linux-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:1.4.0
2 |
3 | RUN \
4 | echo "LINUX TOOLS + COMPILER" && \
5 | apt-get update && \
6 | apt-get install -y --no-install-recommends \
7 | libssl-dev \
8 | openssl \
9 | libtool \
10 | autoconf \
11 | automake \
12 | build-essential \
13 | uuid-dev \
14 | libxi-dev \
15 | libx11-xcb-dev \
16 | libopenal-dev \
17 | libgl1-mesa-dev \
18 | libglw1-mesa-dev \
19 | freeglut3-dev
20 |
21 | #
22 | # llvm
23 | #
24 | ENV CLANG_VERSION=17
25 | RUN \
26 | echo "LLVM + CLANG ${CLANG_VERSION}" && \
27 | wget https://apt.llvm.org/llvm.sh && \
28 | chmod +x llvm.sh && \
29 | ./llvm.sh ${CLANG_VERSION} && \
30 | rm llvm.sh
31 |
32 | ENV CLANG_17_HOME=/usr/lib/llvm-${CLANG_VERSION}
33 | # Possibly get the "-resource-dir" from clang++ -### empty.cpp
34 | ENV CLANG_17_RESOURCE_DIR=/usr/lib/llvm-${CLANG_VERSION}/lib/clang/${CLANG_VERSION}
35 | ENV PATH=${CLANG_17_HOME}/bin:$PATH
--------------------------------------------------------------------------------
/server/docker/Dockerfile.nssdk.1532-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | # Added in version 1.4.4
4 | ENV NINTENDO_SDK_ROOT_15_3_2=${PLATFORMSDK_DIR}/nx-15.3.2
5 | ENV SWITCH_SDK_15_FILENAME=nx64-sdk-15.3.2.tar.gz
6 |
7 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
8 | echo "Switch SDK 15.3.2" && \
9 | mkdir -p ${NINTENDO_SDK_ROOT_15_3_2} && \
10 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${SWITCH_SDK_15_FILENAME} | tar xz -C ${NINTENDO_SDK_ROOT_15_3_2}
--------------------------------------------------------------------------------
/server/docker/Dockerfile.nssdk.1753-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV NINTENDO_SDK_ROOT_17_5=${PLATFORMSDK_DIR}/nx-17.5.3
4 | ENV SWITCH_SDK_17_FILENAME=nx64-sdk-17.5.3.tar.gz
5 |
6 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
7 | echo "Switch SDK 17.5.3" && \
8 | mkdir -p ${NINTENDO_SDK_ROOT_17_5} && \
9 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${SWITCH_SDK_17_FILENAME} | tar xz -C ${NINTENDO_SDK_ROOT_17_5}
--------------------------------------------------------------------------------
/server/docker/Dockerfile.nssdk.1832-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV NINTENDO_SDK_ROOT_18_3=${PLATFORMSDK_DIR}/nx-18.3.2
4 | ENV SWITCH_SDK_18_FILENAME=nx64-sdk-18.3.2.tar.gz
5 |
6 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
7 | echo "Switch SDK 18.3.2" && \
8 | mkdir -p ${NINTENDO_SDK_ROOT_18_3} && \
9 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${SWITCH_SDK_18_FILENAME} | tar xz -C ${NINTENDO_SDK_ROOT_18_3}
--------------------------------------------------------------------------------
/server/docker/Dockerfile.ps4.10500-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV PS4_SDK_10=${PLATFORMSDK_DIR}/ps4-sdk-10.500
4 |
5 | ENV PS4_SDK_10_FILENAME=ps4-sdk-10.500.tar.gz
6 |
7 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
8 | echo "PS4 ${PS4_SDK_10_FILENAME}" && \
9 | mkdir -p ${PS4_SDK_10} && \
10 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${PS4_SDK_10_FILENAME} | tar xz -C ${PS4_SDK_10} --strip-components=1
--------------------------------------------------------------------------------
/server/docker/Dockerfile.ps4.11000-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV PS4_SDK_11_VERSION=11.000
4 | ENV PS4_SDK_11=${PLATFORMSDK_DIR}/ps4-sdk-${PS4_SDK_11_VERSION}
5 |
6 | ENV PS4_SDK_11_FILENAME=ps4-sdk-${PS4_SDK_11_VERSION}.tar.gz
7 |
8 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
9 | echo "PS4 ${PS4_SDK_11_FILENAME}" && \
10 | mkdir -p ${PS4_SDK_11} && \
11 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${PS4_SDK_11_FILENAME} | tar xz -C ${PS4_SDK_11} --strip-components=1
--------------------------------------------------------------------------------
/server/docker/Dockerfile.ps4.12000-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV PS4_SDK_VERSION=12.000
4 | ENV PS4_SDK=${PLATFORMSDK_DIR}/ps4-sdk-${PS4_SDK_VERSION}
5 |
6 | ENV PS4_SDK_FILENAME=ps4-sdk-${PS4_SDK_VERSION}.tar.gz
7 |
8 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
9 | echo "PS4 ${PS4_SDK_FILENAME}" && \
10 | mkdir -p ${PS4_SDK} && \
11 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${PS4_SDK_FILENAME} | tar xz -C ${PS4_SDK} --strip-components=1
12 |
--------------------------------------------------------------------------------
/server/docker/Dockerfile.ps5.10000-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV PS5_SDK_VERSION=10.000
4 | ENV PS5_SDK=${PLATFORMSDK_DIR}/ps5-sdk-${PS5_SDK_VERSION}
5 |
6 | ENV PS5_SDK_FILENAME=ps5-sdk-${PS5_SDK_VERSION}.tar.gz
7 |
8 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
9 | echo "PS5 ${PS5_SDK_FILENAME}" && \
10 | mkdir -p ${PS5_SDK} && \
11 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${PS5_SDK_FILENAME} | tar xz -C ${PS5_SDK} --strip-components=1
12 |
--------------------------------------------------------------------------------
/server/docker/Dockerfile.ps5.8000-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV PS5_SDK_8_VERSION=8.000
4 | ENV PS5_SDK_8=${PLATFORMSDK_DIR}/ps5-sdk-${PS5_SDK_8_VERSION}
5 |
6 | ENV PS5_SDK_8_FILENAME=ps5-sdk-${PS5_SDK_8_VERSION}.tar.gz
7 |
8 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
9 | echo "PS5 ${PS5_SDK_8_FILENAME}" && \
10 | mkdir -p ${PS5_SDK_8} && \
11 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${PS5_SDK_8_FILENAME} | tar xz -C ${PS5_SDK_8} --strip-components=1
--------------------------------------------------------------------------------
/server/docker/Dockerfile.ps5.9000-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-wine-env:1.4.0
2 |
3 | ENV PS5_SDK_9_VERSION=9.000
4 | ENV PS5_SDK_9=${PLATFORMSDK_DIR}/ps5-sdk-${PS5_SDK_9_VERSION}
5 |
6 | ENV PS5_SDK_9_FILENAME=ps5-sdk-${PS5_SDK_9_VERSION}.tar.gz
7 |
8 | RUN --mount=type=secret,id=DM_PACKAGES_URL,required=true \
9 | echo "PS5 ${PS5_SDK_9_FILENAME}" && \
10 | mkdir -p ${PS5_SDK_9} && \
11 | wget -q -O - $(cat /run/secrets/DM_PACKAGES_URL)/${PS5_SDK_9_FILENAME} | tar xz -C ${PS5_SDK_9} --strip-components=1
--------------------------------------------------------------------------------
/server/docker/Dockerfile.wine-env:
--------------------------------------------------------------------------------
1 | FROM europe-west1-docker.pkg.dev/extender-426409/extender-public-registry/extender-base-env:1.4.0
2 |
3 | # Installation notes: https://wiki.winehq.org/Ubuntu
4 | # TODO: Backup the files as descibed here: https://wiki.winehq.org/Ubuntu
5 | RUN \
6 | echo "Wine deps" && \
7 | apt-get update && \
8 | apt-get install -y --no-install-recommends \
9 | python-is-python3 \
10 | openssl \
11 | gnupg1 \
12 | gpg-agent \
13 | build-essential \
14 | software-properties-common \
15 | apt-transport-https \
16 | ca-certificates \
17 | ca-certificates-java \
18 | xvfb \
19 | cabextract && \
20 | apt-get clean autoclean autoremove
21 |
22 | # From https://github.com/scottyhardy/docker-wine/blob/master/Dockerfile
23 |
24 | # Install wine
25 | ARG WINE_BRANCH="stable"
26 | RUN wget -nv -O- https://dl.winehq.org/wine-builds/winehq.key | APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 apt-key add - \
27 | && echo "deb https://dl.winehq.org/wine-builds/ubuntu/ $(grep VERSION_CODENAME= /etc/os-release | cut -d= -f2) main" >> /etc/apt/sources.list \
28 | && dpkg --add-architecture i386 \
29 | && apt-get update \
30 | && DEBIAN_FRONTEND="noninteractive" apt-get install -y --install-recommends winehq-${WINE_BRANCH} \
31 | && rm -rf /var/lib/apt/lists/*
32 |
33 | # Install winetricks
34 | RUN wget -nv -O /usr/bin/winetricks https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks \
35 | && chmod +x /usr/bin/winetricks
36 |
37 | ENV PATH=${PATH}:/opt/wine-stable/bin
38 |
39 | # Disable all debug messages
40 | ENV WINEDEBUG="-all"
41 |
42 | ENV WINEPREFIX="/var/extender/.wine"
43 |
44 | RUN \
45 | echo "Wine Init" && \
46 | mkdir -p ${WINEPREFIX}
47 |
48 | ENV DISPLAY=":1"
49 | RUN \
50 | set -xe && \
51 | WINEDLLOVERRIDES="mscoree,mshtml=" xvfb-run wine wineboot && \
52 | xvfb-run wineserver -w
53 | # xvfb-run wineserver -w && \
54 | # xvfb-run winetricks -q vcrun2015
55 |
56 | RUN \
57 | chown -R extender: $WINEPREFIX
58 |
59 |
60 | #
61 | # llvm
62 | #
63 | ENV CLANG_VERSION=17
64 | RUN \
65 | echo "LLVM + CLANG ${CLANG_VERSION}" && \
66 | wget https://apt.llvm.org/llvm.sh && \
67 | chmod +x llvm.sh && \
68 | ./llvm.sh ${CLANG_VERSION} && \
69 | rm llvm.sh
70 |
71 | ENV CLANG_17_HOME=/usr/lib/llvm-${CLANG_VERSION}
72 | # Possibly get the "-resource-dir" from clang++ -### empty.cpp
73 | ENV CLANG_17_RESOURCE_DIR=/usr/lib/llvm-${CLANG_VERSION}/lib/clang/${CLANG_VERSION}
74 | ENV PATH=${CLANG_17_HOME}/bin:$PATH
75 |
--------------------------------------------------------------------------------
/server/docker/common-services.yml:
--------------------------------------------------------------------------------
1 | services:
2 | test_builder:
3 | platform: linux/amd64
4 | volumes:
5 | - ./../app/:/app/:ro
6 | - ./../configs:/etc/defold/extender:ro
7 | entrypoint: ["java","-Xmx4g","-XX:MaxDirectMemorySize=2g","-jar","/app/extender.jar"]
8 | user: extender
9 | environment:
10 | - EXAMPLE_VAR=1
11 | healthcheck:
12 | # Extender's port is hardcoded here.
13 | test: wget --no-verbose --tries=1 --spider http://localhost:9000/actuator/health
14 | interval: 10s
15 | timeout: 2s
16 | retries: 10
17 | start_period: 5s
18 | common_builder:
19 | platform: linux/amd64
20 | volumes:
21 | - ./../app/:/app/:ro
22 | - ./../app/:/etc/extender/apps/:ro
23 | - ./../configs:/etc/defold/extender:ro
24 | - ${DYNAMO_HOME:-./../app/dummy}:/dynamo_home
25 | entrypoint: ["java","-Xmx4g","-XX:MaxDirectMemorySize=2g","-Dorg.eclipse.jetty.server.Request.maxFormKeys=1500","-jar","/app/extender.jar"]
26 | user: extender
27 | environment:
28 | - DYNAMO_HOME${DYNAMO_HOME:+=/dynamo_home}
29 | - DM_DEBUG_COMMANDS
30 | - DM_DEBUG_DISABLE_PROGUARD
31 | - DM_DEBUG_JOB_FOLDER
32 | - DM_DEBUG_KEEP_JOB_FOLDER
33 | - DM_DEBUG_JOB_UPLOAD
34 | remote_builder:
35 | extends: common_builder
36 | command: ["--spring.config.location=classpath:./,file:/etc/defold/extender/", "--spring.profiles.active=local-dev${STRUCTURED_LOGGING+,logging}"]
37 | test_remote_builder:
38 | extends: test_builder
39 | command: ["--spring.config.location=classpath:./,file:/etc/defold/extender/", "--spring.profiles.active=local-dev"]
--------------------------------------------------------------------------------
/server/envs/.env:
--------------------------------------------------------------------------------
1 | SERVICE_NAME=extender
2 | ZIG_VERSION=0.11.0
3 |
--------------------------------------------------------------------------------
/server/envs/generate_user_env.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | echo "Generate use dynamic environment..."
4 |
5 | ENV_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
6 |
7 | echo "Load common env ..."
8 | source ${ENV_DIR}/.env
9 |
10 |
11 | PLATFORMSDK_DIR=${ENV_DIR}/../../platformsdk
12 | MANIFEST_MERGE_TOOL=${ENV_DIR}/../app/manifestmergetool.jar
13 | SPRING_PROFILES_LOCATION=${ENV_DIR}/../configs
14 |
15 | PATH_TO_JAR=${ENV_DIR}/../app/extender.jar
16 | PID_PATH_NAME=${ENV_DIR}/../app/${SERVICE_NAME}.pid
17 | LOG_DIRECTORY=${ENV_DIR}/../app/logs
18 | STDOUT_LOG=${LOG_DIRECTORY}/stdout.log
19 | ERROR_LOG=${LOG_DIRECTORY}/error.log
20 | EXTENDER_SDK_LOCATION=${ENV_DIR}/../app/sdk
21 |
22 | DOTNET_ROOT=${ENV_DIR}/../app/dotnet
23 | DOTNET_CLI_HOME=${DOTNET_ROOT}
24 | DOTNET_VERSION_FILE=${DOTNET_ROOT}/dotnet_version
25 | NUGET_PACKAGES=${ENV_DIR}/../app/.nuget
26 |
27 | ZIG_PATH_0_11=${PLATFORMSDK_DIR}/zig-${ZIG_VERSION}
28 |
29 |
30 | OUTPUT_FILE=$ENV_DIR/user.env
31 |
32 | if [[ -z $1 ]]; then
33 | echo "Load macos environment..."
34 | source ${ENV_DIR}/macos.env
35 | APPENDED_PATH=${PLATFORMSDK_DIR}/XcodeDefault${XCODE_16_VERSION}.xctoolchain/usr/bin:/usr/local/bin:${PATH}
36 | else
37 | echo "Load $1 environment..."
38 | source ${ENV_DIR}/$1.env
39 | fi
40 |
41 | [ -f "$OUTPUT_FILE" ] && echo "Remove old user.env" && rm "$OUTPUT_FILE"
42 |
43 | if [[ "" == "${JAVA_HOME}" ]]; then
44 | # select java home for JDK 21
45 | JAVA_HOME=`/usr/libexec/java_home -v 21`
46 | fi
47 |
48 | echo "ENV_DIR=${ENV_DIR}" > $OUTPUT_FILE
49 | echo "JAVA_HOME=${JAVA_HOME}" >> $OUTPUT_FILE
50 | echo "PLATFORMSDK_DIR=${PLATFORMSDK_DIR}" >> $OUTPUT_FILE
51 | echo "MANIFEST_MERGE_TOOL=${MANIFEST_MERGE_TOOL}" >> $OUTPUT_FILE
52 | echo "SPRING_PROFILES_LOCATION=${SPRING_PROFILES_LOCATION}" >> $OUTPUT_FILE
53 | echo "PATH_TO_JAR=${PATH_TO_JAR}" >> $OUTPUT_FILE
54 | echo "PID_PATH_NAME=${PID_PATH_NAME}" >> $OUTPUT_FILE
55 | echo "LOG_DIRECTORY=${LOG_DIRECTORY}" >> $OUTPUT_FILE
56 | echo "STDOUT_LOG=${STDOUT_LOG}" >> $OUTPUT_FILE
57 | echo "ERROR_LOG=${ERROR_LOG}" >> $OUTPUT_FILE
58 | echo "EXTENDER_SDK_LOCATION=${EXTENDER_SDK_LOCATION}" >> $OUTPUT_FILE
59 | echo "DOTNET_ROOT=${DOTNET_ROOT}" >> $OUTPUT_FILE
60 | echo "DOTNET_CLI_HOME=${DOTNET_CLI_HOME}" >> $OUTPUT_FILE
61 | echo "DOTNET_VERSION_FILE=${DOTNET_VERSION_FILE}" >> $OUTPUT_FILE
62 | echo "NUGET_PACKAGES=${NUGET_PACKAGES}" >> $OUTPUT_FILE
63 | # Added 1.4.9
64 | echo "ZIG_PATH_0_11=${ZIG_PATH_0_11}" >> $OUTPUT_FILE
65 |
66 | echo "PATH=\"${APPENDED_PATH}\"" >> $OUTPUT_FILE
67 |
68 | echo "Generation completed."
69 |
--------------------------------------------------------------------------------
/server/envs/macos.env:
--------------------------------------------------------------------------------
1 | SWIFT_5_5_VERSION=5.5
2 | IOS_VERSION_MIN=11.0
3 | MACOS_VERSION_MIN=10.13
4 | XCODE_15_VERSION=15.4
5 | XCODE_15_CLANG_VERSION=15.0.0
6 | MACOS_14_VERSION=14.5
7 | IOS_17_VERSION=17.5
8 | XCODE_16_VERSION=16.2
9 | XCODE_16_CLANG_VERSION=16.0.0
10 | MACOS_15_VERSION=15.2
11 | IOS_18_VERSION=18.2
12 |
--------------------------------------------------------------------------------
/server/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.logging.stacktrace=all
2 | org.gradle.logging.level=info
--------------------------------------------------------------------------------
/server/manifestmergetool/README.md:
--------------------------------------------------------------------------------
1 | Run tests:
2 |
3 | ```sh
4 | # Run tests with info-level logging
5 | gradle test -i
6 | ```
--------------------------------------------------------------------------------
/server/manifestmergetool/src/main/java/com/defold/manifestmergetool/MergePolicy.java:
--------------------------------------------------------------------------------
1 | package com.defold.manifestmergetool;
2 |
3 | public enum MergePolicy {
4 | KEEP,
5 | MERGE,
6 | REPLACE;
7 |
8 | public static MergePolicy fromString(String s) {
9 | switch (s) {
10 | case "keep":
11 | return MergePolicy.KEEP;
12 | case "replace":
13 | return MergePolicy.REPLACE;
14 | default:
15 | return MergePolicy.MERGE;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/server/manifestmergetool/src/main/resources/PropertyList-1.0.dtd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/server/scripts/standalone/service-standalone.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4 |
5 | # enable mode when all variables exports by default
6 | set -a
7 | echo "Load common env ..."
8 | source $SCRIPT_DIR/../../envs/.env
9 |
10 | echo "Load user env ..."
11 | source $SCRIPT_DIR/../../envs/user.env
12 |
13 | if [[ -z ${ENV_PROFILE} ]]; then
14 | echo "Load macos environment..."
15 | source $SCRIPT_DIR/../../envs/macos.env
16 | else
17 | echo "Load ${ENV_PROFILE} environment..."
18 | source $SCRIPT_DIR/../../envs/${ENV_PROFILE}.env
19 | fi
20 |
21 | # disable variable export mode
22 | set +a
23 |
24 | PROFILE=$2
25 | if [[ -z ${PROFILE} ]]; then
26 | echo "No extender profile provided."
27 | PROFILE="standalone-dev"
28 | fi
29 | echo "Using profile ${PROFILE}."
30 |
31 | start_service() {
32 | echo "${SERVICE_NAME} starting..."
33 |
34 | # Check if pid file already exists and if the process in the pid file is running
35 | if [[ -f ${PID_PATH_NAME} ]]; then
36 | PID=$(cat ${PID_PATH_NAME});
37 | if [[ -n "$(ps -p ${PID} -o pid=)" ]]; then
38 | echo "Error! ${SERVICE_NAME} is already running, exiting."
39 | exit 2
40 | else
41 | echo "Warning: PID file exists but no process running, removing PID file."
42 | rm ${PID_PATH_NAME}
43 | fi
44 | fi
45 |
46 | echo "Running: java -Xmx4g -XX:MaxDirectMemorySize=2g -Dorg.eclipse.jetty.server.Request.maxFormKeys=1500 -jar ${PATH_TO_JAR} --extender.sdk.location=${EXTENDER_SDK_LOCATION} --spring.config.location=classpath:./,file:${SPRING_PROFILES_LOCATION}/ --spring.profiles.active=${PROFILE}${STRUCTURED_LOGGING+,logging} ${STRUCTURED_LOGGING+--logging.config=${SPRING_PROFILES_LOCATION}/extender-logging.xml} >> ${STDOUT_LOG} 2>> ${ERROR_LOG} < /dev/null &"
47 | java -Xmx4g -XX:MaxDirectMemorySize=2g -Dorg.eclipse.jetty.LEVEL=DEBUG -Dorg.eclipse.jetty.server.Request.maxFormKeys=1500 -jar ${PATH_TO_JAR} --extender.sdk.location="${EXTENDER_SDK_LOCATION}" --spring.config.location=classpath:./,file:${SPRING_PROFILES_LOCATION}/ --spring.profiles.active=${PROFILE}${STRUCTURED_LOGGING+,logging} ${STRUCTURED_LOGGING+--logging.config=${SPRING_PROFILES_LOCATION}/extender-logging.xml} >> ${STDOUT_LOG} 2>> ${ERROR_LOG} < /dev/null &
48 |
49 |
50 | echo $! > ${PID_PATH_NAME}
51 | echo "${SERVICE_NAME} started."
52 | }
53 |
54 | stop_service() {
55 | if [[ -f ${PID_PATH_NAME} ]]; then
56 | PID=$(cat ${PID_PATH_NAME});
57 | echo "${SERVICE_NAME} stopping..."
58 | kill ${PID};
59 | echo "${SERVICE_NAME} stopped."
60 | rm ${PID_PATH_NAME}
61 | else
62 | echo "Warning: ${SERVICE_NAME} is not running, no PID file."
63 | fi
64 | }
65 |
66 | case $1 in
67 | start)
68 | start_service
69 | ;;
70 | stop)
71 | stop_service
72 | ;;
73 | restart)
74 | stop_service
75 | start_service
76 | ;;
77 | esac
78 |
--------------------------------------------------------------------------------
/server/scripts/stop-test-server.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ "$APPLICATION" == "" ]; then
4 | APPLICATION="extender-test"
5 | fi
6 |
7 | echo "stop-test-server.sh: Stopping ${APPLICATION}:"
8 |
9 | docker compose -p $APPLICATION down
10 |
11 | echo "stop-test-server.sh: Test server ${APPLICATION} exited"
12 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/AppManifestConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public class AppManifestConfiguration {
7 | public Map platforms = new HashMap<>();
8 | public Map context = new HashMap<>();
9 | }
10 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/AppManifestPlatformConfig.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | class AppManifestPlatformConfig {
7 | public Map context = new HashMap<>();
8 | }
9 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/BuilderConstants.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | public class BuilderConstants {
4 | public static final String BUILD_RESULT_FILENAME = "build.zip";
5 | public static final String BUILD_ERROR_FILENAME = "error.txt";
6 |
7 | public enum JobStatus {
8 | NOT_FOUND,
9 | SUCCESS,
10 | ERROR
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/Configuration.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.Map;
4 |
5 | public class Configuration {
6 | public Map platforms;
7 | public Map context;
8 | public String main;
9 | public WhitelistConfig whitelist; // Already a deprecated var! TODO: Remove
10 | }
11 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/ExtenderApplication.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 |
8 | import org.springframework.core.env.Environment;
9 | import org.springframework.scheduling.annotation.EnableAsync;
10 | import org.springframework.scheduling.annotation.EnableScheduling;
11 |
12 | @SpringBootApplication
13 | @EnableAsync
14 | @EnableScheduling
15 | public class ExtenderApplication {
16 |
17 | private static final Logger LOGGER = LoggerFactory.getLogger(ExtenderApplication.class);
18 |
19 | private final Environment environment;
20 |
21 | public ExtenderApplication(Environment environment) {
22 | this.environment = environment;
23 | }
24 |
25 | public static void main(String[] args) {
26 | SpringApplication.run(ExtenderApplication.class, args);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/ExtenderException.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | public class ExtenderException extends Exception {
4 | private final String output;
5 |
6 | public ExtenderException(String output) {
7 | super(output);
8 | this.output = output;
9 | }
10 |
11 | public ExtenderException(Exception e, String output) {
12 | super(e);
13 | this.output = output;
14 | }
15 |
16 | String getOutput() {
17 | return output;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/ExtenderYamlSafeConstructor.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import org.yaml.snakeyaml.LoaderOptions;
4 | import org.yaml.snakeyaml.constructor.Construct;
5 | import org.yaml.snakeyaml.constructor.Constructor;
6 | import org.yaml.snakeyaml.nodes.Tag;
7 |
8 | class ExtenderYamlSafeConstructor extends Constructor {
9 |
10 | public ExtenderYamlSafeConstructor(LoaderOptions loadingConfig) {
11 | super(loadingConfig);
12 | Construct c = this.yamlConstructors.get(null); // ConstructYamlObject
13 |
14 | this.yamlConstructors.put(new Tag(Tag.PREFIX + "com.defold.extender.Configuration"), c);
15 | this.yamlConstructors.put(new Tag(Tag.PREFIX + "com.defold.extender.ManifestConfiguration"), c);
16 | this.yamlConstructors.put(new Tag(Tag.PREFIX + "com.defold.extender.AppManifestConfiguration"), c);
17 | this.yamlConstructors.put(new Tag(Tag.PREFIX + "com.defold.extender.PlatformConfig"), c);
18 | this.yamlConstructors.put(new Tag(Tag.PREFIX + "com.defold.extender.AppManifestPlatformConfig"), c);
19 | this.yamlConstructors.put(new Tag(Tag.PREFIX + "com.defold.extender.ManifestPlatformConfig"), c);
20 | this.yamlConstructors.put(new Tag(Tag.PREFIX + "com.defold.extender.WhitelistConfig"), c);
21 |
22 | this.yamlConstructors.put(null, undefinedConstructor);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/FrameworkUtil.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.io.File;
4 | import java.util.List;
5 |
6 | import com.defold.extender.process.ProcessUtils;
7 |
8 | public class FrameworkUtil {
9 | /**
10 | * Check if a framework is dynamically linked
11 | * Cocoapods (written in Ruby) uses the "macho" gem to do the same thing:
12 | * https://github.com/CocoaPods/CocoaPods/blob/master/lib/cocoapods/xcode/linkage_analyzer.rb#L16
13 | * https://github.com/Homebrew/ruby-macho/
14 | * @param framework The framework to check
15 | * @return true if dynamically linked
16 | */
17 | public static boolean isDynamicallyLinked(File framework) throws ExtenderException {
18 | String filename = framework.getName();
19 | // static library
20 | if (filename.endsWith(".a")) {
21 | return false;
22 | }
23 | if (framework.isDirectory() && filename.endsWith(".framework")) {
24 | String frameworkName = filename.replace(".framework", "");
25 | File frameworkBinary = new File(framework, frameworkName);
26 | String output = ProcessUtils.execCommand(List.of(
27 | "file",
28 | frameworkBinary.getAbsolutePath()), null, null);
29 | if (output.contains("dynamically linked shared library")) {
30 | return true;
31 | }
32 | }
33 | return false;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/ManifestConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.Map;
4 |
5 | public class ManifestConfiguration {
6 | public Map platforms;
7 | public String name;
8 | }
9 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/ManifestPlatformConfig.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | class ManifestPlatformConfig {
7 | public Map bundle = new HashMap<>(); // used on the content pipeline side (2023-02-08: UNUSED?)
8 | public Map context = new HashMap<>();
9 | }
10 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/PlatformConfig.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.HashMap;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | public class PlatformConfig {
8 |
9 | public Map env = new HashMap<>();
10 | public Map context = new HashMap<>();
11 | public String exePrefix; // deprecated
12 | public String exeExt; // deprecated
13 | public String writeLibPattern;
14 | public String writeShLibPattern;
15 | public String writeExePattern;
16 | public String zipContentPattern;
17 | public String shlibRe;
18 | public String stlibRe;
19 | public String sourceRe;
20 | public String javaSourceRe;
21 | public String compileCmd;
22 | public String linkCmd;
23 | public List linkCmds;
24 | public String libCmd;
25 | public String dxCmd;
26 | public String aapt2compileCmd;
27 | public String aapt2linkCmd;
28 | public String rjavaCmd;
29 | public String manifestName;
30 | public String manifestMergeCmd;
31 | public String bitcodeStripCmd;
32 | public String proGuardSourceRe;
33 | public String proGuardCmd;
34 | public String windresCmd;
35 | public String symbolCmd;
36 | public String symbolsPattern;
37 | public List allowedLibs;
38 | public List allowedFlags;
39 | public List allowedSymbols;
40 |
41 | public String protoEngineCxxCmd;
42 | public String protoPipelineCmd;
43 | public String protoPipelineOutputRe;
44 |
45 | // C++
46 | public String compileCmdCXX;
47 | public String compileCmdCXXSh;
48 | public String linkCmdCXX;
49 | public String linkCmdCXXSh;
50 |
51 | // Java
52 | public String javacCmd;
53 | public String jarCmd;
54 |
55 | // Zig
56 | public String zigSourceRe;
57 | public String zigCompileCmd;
58 |
59 | // Swift
60 | public String emitSwiftHeaderCmd;
61 | public String emitSwiftModuleCmd;
62 | public String compileSwiftCmd;
63 |
64 | // C#
65 | public String csSourceRe;
66 |
67 | // Deprecated
68 | public String mtCmd; // Deprecated, use windres instead (Deprecated at 1.2.135)
69 | }
70 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/TemplateExecutor.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import com.defold.extender.log.Markers;
4 | import com.samskivert.mustache.Mustache;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | public class TemplateExecutor {
14 | private static final Logger LOGGER = LoggerFactory.getLogger(TemplateExecutor.class);
15 |
16 | public String execute(String template, Map context) {
17 | try {
18 | String result = Mustache.compiler().compile(template).execute(context);
19 | while (!result.equals(template)) {
20 | template = result;
21 | result = Mustache.compiler().compile(template).execute(context);
22 | }
23 | return result;
24 | } catch (Exception e) {
25 | LOGGER.error(Markers.COMPILATION_ERROR, String.format("Failed to substitute string '%s'", (String)template));
26 | ExtenderUtil.debugPrint(context, 0);
27 | throw e;
28 | }
29 | }
30 |
31 | public List execute(List templates, Map context) {
32 | List out = new ArrayList<>();
33 | for (String template : templates) {
34 | try {
35 | out.add(this.execute(template, context));
36 | } catch (Exception e) {
37 | LOGGER.error(Markers.COMPILATION_ERROR, String.format("Failed to substitute string in list [..., '%s', ...]", (String)template));
38 | ExtenderUtil.debugPrint(context, 0);
39 | throw e;
40 | }
41 | }
42 | return out;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/Timer.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | public class Timer {
4 | private long time;
5 |
6 | public long start() {
7 | long now = System.currentTimeMillis();
8 | long t = now - time;
9 | time = now;
10 | return t;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/TreePrinter.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.lang.StringBuilder;
4 | import java.io.File;
5 |
6 | public class TreePrinter {
7 |
8 | /**
9 | * Pretty print the directory tree and its file names.
10 | * https://stackoverflow.com/a/13130974/1266551
11 | *
12 | * @param folder
13 | * @return
14 | */
15 | public static String printDirectoryTree(File folder) {
16 | if (!folder.isDirectory()) {
17 | throw new IllegalArgumentException("folder is not a Directory");
18 | }
19 | int indent = 0;
20 | StringBuilder sb = new StringBuilder();
21 | printDirectoryTree(folder, indent, sb);
22 | return sb.toString();
23 | }
24 |
25 | private static void printDirectoryTree(File folder, int indent, StringBuilder sb) {
26 | if (!folder.isDirectory()) {
27 | throw new IllegalArgumentException("folder is not a Directory");
28 | }
29 | sb.append(getIndentString(indent));
30 | sb.append("+--");
31 | sb.append(folder.getName());
32 | sb.append("/");
33 | sb.append("\n");
34 | for (File file : folder.listFiles()) {
35 | if (file.isDirectory()) {
36 | printDirectoryTree(file, indent + 1, sb);
37 | } else {
38 | printFile(file, indent + 1, sb);
39 | }
40 | }
41 |
42 | }
43 |
44 | private static void printFile(File file, int indent, StringBuilder sb) {
45 | sb.append(getIndentString(indent));
46 | sb.append("+--");
47 | sb.append(file.getName());
48 | sb.append("\n");
49 | }
50 |
51 | private static String getIndentString(int indent) {
52 | StringBuilder sb = new StringBuilder();
53 | for (int i = 0; i < indent; i++) {
54 | sb.append("| ");
55 | }
56 | return sb.toString();
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/WhitelistConfig.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.regex.Pattern;
6 |
7 | class WhitelistConfig {
8 | public Map context = new HashMap<>();
9 |
10 | // Used to verify C++ defines before adding them on the command line
11 | // DEFINE
12 | // DEFINE=
13 | // DEFINE=123value
14 | public String defineRe = "^([A-Za-z_][A-Za-z0-9_]+)(|=|=[A-Za-z0-9_]+)$";
15 | public String libraryRe = "(\\w[\\w\\.+-]*)";
16 |
17 | public WhitelistConfig() {
18 | context.put("arg", "[a-zA-Z][a-zA-Z0-9-_]+");
19 | context.put("comma_separated_arg", "[a-zA-Z][a-zA-Z0-9-_]+");
20 | context.put("number", "[0-9]+");
21 | context.put("warning", "[a-zA-Z][a-zA-Z0-9-_+]+");
22 | }
23 |
24 | static Pattern compile(String re) {
25 | return Pattern.compile(String.format("^(%s)$", re));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/CacheEntry.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | public class CacheEntry {
4 |
5 | private String key;
6 | private String path;
7 | private boolean cached;
8 |
9 | @SuppressWarnings("unused")
10 | CacheEntry() {
11 | }
12 |
13 | public CacheEntry(final String key, final String path, final boolean cached) {
14 | this.key = key;
15 | this.path = path;
16 | this.cached = cached;
17 | }
18 |
19 | public String getKey() {
20 | return key;
21 | }
22 |
23 | public void setKey(final String key) {
24 | this.key = key;
25 | }
26 |
27 | public String getPath() {
28 | return path;
29 | }
30 |
31 | public void setPath(final String path) {
32 | this.path = path;
33 | }
34 |
35 | public boolean isCached() {
36 | return cached;
37 | }
38 |
39 | public void setCached(final boolean cached) {
40 | this.cached = cached;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/CacheKeyGenerator.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | import org.springframework.stereotype.Service;
4 |
5 | import java.io.BufferedInputStream;
6 | import java.io.File;
7 | import java.io.FileInputStream;
8 | import java.io.IOException;
9 | import java.math.BigInteger;
10 | import java.security.MessageDigest;
11 | import java.security.NoSuchAlgorithmException;
12 |
13 | @Service
14 | public class CacheKeyGenerator {
15 |
16 | private static final String SHA256 = "SHA-256";
17 |
18 | public String generate(File file) throws IOException {
19 | int count;
20 |
21 | MessageDigest digest = getDigest();
22 |
23 | try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
24 | byte[] buffer = new byte[8192];
25 | while ((count = bis.read(buffer)) > 0) {
26 | digest.update(buffer, 0, count);
27 | }
28 | }
29 |
30 | // Note: If you change this, the format of the cache is changed
31 | // See: ./client/src/main/java/com/defold/extender/client/ExtenderClient.java (which is then bundled in bob.jar)
32 | // and native_extensions.clj in the editor for the equivalent parts
33 | byte[] bytes = digest.digest();
34 | return new BigInteger(1, bytes).toString(16);
35 | }
36 |
37 | private MessageDigest getDigest() {
38 | try {
39 | return MessageDigest.getInstance(SHA256);
40 | } catch (NoSuchAlgorithmException e) {
41 | throw new IllegalStateException("Algorithm SHA-256 is not supported", e);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/DataCache.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 |
7 | public interface DataCache {
8 | InputStream get(String key);
9 | boolean exists(String key);
10 | void touch(String key);
11 | void put(String key, File file) throws IOException;
12 | }
13 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/DataCacheException.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | class DataCacheException extends Exception {
4 | public DataCacheException(String message) {
5 | super(message);
6 | }
7 |
8 | public DataCacheException(String message, Throwable cause) {
9 | super(message, cause);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/DataCacheFactory.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.stereotype.Service;
7 |
8 | import java.io.IOException;
9 |
10 | @Service
11 | public class DataCacheFactory {
12 |
13 | private static final Logger LOGGER = LoggerFactory.getLogger(DataCacheFactory.class);
14 |
15 | private static final String STORE_TYPE_GCP = "GCP";
16 | private static final String STORE_TYPE_LOCAL = "LOCAL";
17 |
18 | private final boolean isEnabled;
19 | private final String storeType;
20 | private final String prefix;
21 | private final String baseDirectory;
22 | private final String bucketName;
23 |
24 | public DataCacheFactory(@Value("${extender.cache.enabled}") boolean isEnabled,
25 | @Value("${extender.cache.type:}") String storeType,
26 | @Value("${extender.cache.prefix:}") String prefix,
27 | @Value("${extender.cache.local.basedir:}") String baseDirectory,
28 | @Value("${extender.cache.gcp.bucket:}") String bucketName) {
29 | this.isEnabled = isEnabled;
30 | this.storeType = storeType;
31 | this.prefix = prefix;
32 | this.baseDirectory = baseDirectory;
33 | this.bucketName = bucketName;
34 | }
35 |
36 | public DataCache createCache() {
37 | if (!isEnabled) {
38 | LOGGER.info("Creating dummy data cache since cache is disabled");
39 | return new DummyDataCache();
40 | }
41 |
42 | if (STORE_TYPE_GCP.equals(storeType)) {
43 | LOGGER.info("Creating GCP cache with bucket name {}", bucketName);
44 | return new GCPDataCache(bucketName, prefix);
45 | } else if (STORE_TYPE_LOCAL.equals(storeType)) {
46 | LOGGER.info("Creating local disk cache in directory {}", baseDirectory);
47 | try {
48 | return new LocalDiskDataCache(baseDirectory);
49 | } catch (IOException e) {
50 | throw new IllegalArgumentException("Illegal base directory for local disk cache: " + baseDirectory, e);
51 | }
52 | } else {
53 | throw new IllegalArgumentException(String.format("No cache store of type %s implemented!", storeType));
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/DummyDataCache.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | import java.io.File;
4 | import java.io.InputStream;
5 |
6 | public class DummyDataCache implements DataCache {
7 |
8 | @Override
9 | public InputStream get(final String key) {
10 | return null;
11 | }
12 |
13 | @Override
14 | public boolean exists(String key) {
15 | return false;
16 | }
17 |
18 | @Override
19 | public void touch(String key) {
20 | // Do nothing
21 | }
22 |
23 | @Override
24 | public void put(final String key, final File file) {
25 | // Do nothing
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/info/CacheInfoFileParser.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache.info;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import org.springframework.stereotype.Service;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 |
10 | @Service
11 | public class CacheInfoFileParser {
12 |
13 | private ObjectMapper objectMapper = new ObjectMapper();
14 |
15 | public CacheInfoWrapper parse(final File file) throws IOException {
16 | return objectMapper.readerFor(CacheInfoWrapper.class).readValue(file);
17 | }
18 |
19 | public CacheInfoWrapper parse(final InputStream inputStream) throws IOException {
20 | return objectMapper.readerFor(CacheInfoWrapper.class).readValue(inputStream);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/info/CacheInfoFileWriter.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache.info;
2 |
3 | import com.defold.extender.cache.CacheEntry;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import org.springframework.stereotype.Service;
6 |
7 | import java.io.IOException;
8 | import java.io.OutputStream;
9 | import java.util.List;
10 |
11 | @Service
12 | public class CacheInfoFileWriter {
13 |
14 | private ObjectMapper objectMapper = new ObjectMapper();
15 |
16 | public void write(int version, String hashType, List entries, OutputStream outputStream) throws IOException {
17 | objectMapper.writer().writeValue(outputStream, new CacheInfoWrapper(version, hashType, entries));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/cache/info/CacheInfoWrapper.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache.info;
2 |
3 | import com.defold.extender.cache.CacheEntry;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 |
6 | import java.util.List;
7 |
8 | public class CacheInfoWrapper {
9 |
10 | private int version;
11 |
12 | private String hashType;
13 |
14 | @JsonProperty("files")
15 | private List entries;
16 |
17 | @SuppressWarnings("unused")
18 | CacheInfoWrapper() {
19 | }
20 |
21 | // Used when writing to disc
22 | CacheInfoWrapper(int version, String hashType, List entries) {
23 | this.version = version;
24 | this.hashType = hashType;
25 | this.entries = entries;
26 | }
27 |
28 | public List getEntries() {
29 | return entries;
30 | }
31 |
32 | public int getVersion() {
33 | return version;
34 | }
35 |
36 | public String getHashType() {
37 | return hashType;
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/log/ExtenderLogEnhancer.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.log;
2 |
3 | import com.google.cloud.logging.LogEntry.Builder;
4 | import com.google.cloud.logging.LoggingEnhancer;
5 | import com.google.cloud.logging.logback.LoggingEventEnhancer;
6 |
7 |
8 | import ch.qos.logback.classic.spi.ILoggingEvent;
9 |
10 | public class ExtenderLogEnhancer implements LoggingEnhancer, LoggingEventEnhancer {
11 | @Override
12 | public void enhanceLogEntry(Builder builder) {
13 | if (ExtenderLogEnhancerConfiguration.isInitialized()) {
14 | builder.setResource(ExtenderLogEnhancerConfiguration.getMonitoredResource());
15 | }
16 | }
17 |
18 | @Override
19 | public void enhanceLogEntry(Builder builder, ILoggingEvent e) {
20 | enhanceLogEntry(builder);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/log/ExtenderLogEnhancerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.log;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
5 | import org.springframework.core.env.Environment;
6 | import org.springframework.stereotype.Component;
7 |
8 | import com.google.cloud.MonitoredResource;
9 | import com.google.cloud.spring.core.DefaultGcpProjectIdProvider;
10 |
11 | @Component
12 | @ConditionalOnProperty(name = "spring.cloud.gcp.logging.enabled", havingValue = "true")
13 | public class ExtenderLogEnhancerConfiguration {
14 |
15 | private String instanceName;
16 | private String applicationName;
17 | private String instanceLocation;
18 | private String projectId;
19 | private MonitoredResource monitoredResource;
20 |
21 | private static ExtenderLogEnhancerConfiguration instance;
22 | private ExtenderLogEnhancerConfiguration(@Autowired Environment env) {
23 | if (instance != null) {
24 | throw new IllegalStateException("ExtenderLogEnhancerConfiguration already initialized");
25 | }
26 | instance = this;
27 | this.instanceName = env.getProperty("management.metrics.tags.instance", "unknown");
28 | this.applicationName = env.getProperty("management.metrics.tags.application", "unknown");
29 | this.instanceLocation = env.getProperty("extender.logging.instance-location", "europe-west1-b");
30 | this.projectId = new DefaultGcpProjectIdProvider().getProjectId();
31 | this.monitoredResource = MonitoredResource.newBuilder("generic_node")
32 | .addLabel("project_id", projectId)
33 | .addLabel("location", instanceLocation)
34 | .addLabel("namespace", applicationName)
35 | .addLabel("node_id", instanceName)
36 | .build();
37 | }
38 |
39 | public static boolean isInitialized() {
40 | return instance != null;
41 | }
42 |
43 | public static MonitoredResource getMonitoredResource() {
44 | return instance != null ? instance.monitoredResource : null;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/log/Markers.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.log;
2 |
3 | import org.slf4j.Marker;
4 | import org.slf4j.MarkerFactory;
5 |
6 | public class Markers {
7 | public static final Marker SERVER_ERROR = MarkerFactory.getMarker("SERVER_ERROR");
8 | public static final Marker COMPILATION_ERROR = MarkerFactory.getMarker("COMPILATION_ERROR");
9 | public static final Marker INSTANCE_MANAGER_ERROR = MarkerFactory.getMarker("INSTANCE_MANAGER_ERROR");
10 | public static final Marker CACHE_ERROR = MarkerFactory.getMarker("CACHE_ERROR");
11 | }
12 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/process/ProcessUtils.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.process;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.Arrays;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.stream.Collectors;
9 |
10 | import com.defold.extender.ExtenderException;
11 |
12 | public class ProcessUtils {
13 | public static String execCommand(String command) throws ExtenderException {
14 | return execCommand(command, null);
15 | }
16 |
17 | public static String execCommand(String command, File cwd) throws ExtenderException {
18 | return execCommand(command, cwd, null);
19 | }
20 |
21 | public static String execCommand(String command, File cwd, Map env) throws ExtenderException {
22 | List args = Arrays.stream(command.split(" "))
23 | .filter(s -> !s.isEmpty())
24 | .collect(Collectors.toList());
25 | return execCommand(args, cwd, env);
26 | }
27 |
28 | public static String execCommand(List args, File cwd, Map env) throws ExtenderException {
29 | ProcessExecutor pe = new ProcessExecutor();
30 |
31 | if (cwd != null) {
32 | pe.setCwd(cwd);
33 | }
34 | if (env != null) {
35 | pe.putEnv(env);
36 | }
37 |
38 | try {
39 | if (pe.execute(args) != 0) {
40 | throw new ExtenderException(pe.getOutput());
41 | }
42 | } catch (IOException | InterruptedException e) {
43 | throw new ExtenderException(e, pe.getOutput());
44 | }
45 |
46 | return pe.getOutput();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/remote/RemoteBuildException.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.remote;
2 |
3 | public class RemoteBuildException extends RuntimeException {
4 |
5 | public RemoteBuildException(String message, Throwable cause) {
6 | super(message, cause);
7 | }
8 |
9 | public RemoteBuildException(String message) {
10 | super(message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/remote/RemoteHostConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.remote;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.springframework.boot.context.properties.ConfigurationProperties;
7 | import org.springframework.stereotype.Component;
8 |
9 | @Component
10 | @ConfigurationProperties(prefix = "extender.remote-builder")
11 | public class RemoteHostConfiguration {
12 | private Map platforms = new HashMap<>();
13 |
14 | public Map getPlatforms() {
15 | return platforms;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/remote/RemoteInstanceConfig.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.remote;
2 |
3 | public class RemoteInstanceConfig {
4 | private String url;
5 | private String instanceId;
6 | private boolean alwaysOn;
7 |
8 | public RemoteInstanceConfig(String url, String instanceId, boolean alwaysOn) {
9 | this.url = url;
10 | this.instanceId = instanceId;
11 | this.alwaysOn = alwaysOn;
12 | }
13 |
14 | public String getUrl() {
15 | return url;
16 | }
17 |
18 | public String getInstanceId() {
19 | return instanceId;
20 | }
21 |
22 | public boolean getAlwaysOn() {
23 | return alwaysOn;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/DefoldSdkServiceConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services;
2 |
3 | import java.nio.file.Path;
4 |
5 | import org.springframework.boot.context.properties.ConfigurationProperties;
6 | import org.springframework.stereotype.Component;
7 |
8 | import lombok.AllArgsConstructor;
9 | import lombok.Builder;
10 | import lombok.experimental.SuperBuilder;
11 | import lombok.Data;
12 | import lombok.NoArgsConstructor;
13 |
14 | @Data
15 | @Component
16 | @ConfigurationProperties(prefix = "extender.sdk")
17 | @NoArgsConstructor
18 | @AllArgsConstructor
19 | @SuperBuilder(toBuilder = true)
20 | public class DefoldSdkServiceConfiguration {
21 | private Path location;
22 | private String[] sdkUrls;
23 | private String[] mappingsUrls;
24 | private int cacheSize;
25 | @Builder.Default private int mappingsCacheSize = 20;
26 | // retry count in case of checksum validation fail
27 | @Builder.Default private int maxVerificationRetryCount = 3;
28 | private boolean cacheClearOnExit;
29 | private boolean enableSdkVerification;
30 | }
31 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/GradleService.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import org.springframework.stereotype.Service;
9 |
10 | import com.defold.extender.ExtenderException;
11 |
12 | import io.micrometer.core.instrument.Gauge;
13 | import io.micrometer.core.instrument.MeterRegistry;
14 | import io.micrometer.core.instrument.binder.BaseUnits;
15 |
16 | @Service
17 | public class GradleService {
18 | private final GradleServiceInterface gradleService;
19 |
20 | public GradleService(GradleServiceInterface service,
21 | MeterRegistry registry) {
22 | gradleService = service;
23 | Gauge.builder("extender.job.gradle.cacheSize", this, GradleService::getCacheSize).baseUnit(BaseUnits.BYTES).register(registry);
24 | }
25 |
26 | public List resolveDependencies(Map env, File cwd, File buildDirectory, Boolean useJetifier, List outputFiles)
27 | throws IOException, ExtenderException {
28 | return gradleService.resolveDependencies(env, cwd, buildDirectory, useJetifier, outputFiles);
29 | }
30 |
31 | public long getCacheSize() {
32 | try {
33 | return gradleService.getCacheSize();
34 | } catch (IOException exc) {
35 | return 0;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/GradleServiceInterface.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import com.defold.extender.ExtenderException;
9 |
10 | public interface GradleServiceInterface {
11 | // Resolve dependencies, download them, extract to
12 | public List resolveDependencies(Map env, File cwd, File buildDirectory, Boolean useJetifier, List outputFiles) throws IOException, ExtenderException;
13 |
14 | public long getCacheSize() throws IOException;
15 | }
16 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/MockGradleService.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
9 | import org.springframework.stereotype.Service;
10 |
11 | import com.defold.extender.ExtenderException;
12 |
13 | @Service
14 | @ConditionalOnProperty(name = "extender.gradle.enabled", havingValue = "false", matchIfMissing = true)
15 | public class MockGradleService implements GradleServiceInterface {
16 | @Override
17 | public List resolveDependencies(Map env, File cwd, File buildDirectory, Boolean useJetifier, List outputFiles)
18 | throws IOException, ExtenderException {
19 | return List.of();
20 | }
21 |
22 | @Override
23 | public long getCacheSize() throws IOException {
24 | return 0;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/cocoapods/LanguageSet.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.cocoapods;
2 |
3 | import java.util.LinkedHashSet;
4 | import java.util.List;
5 | import java.util.Set;
6 |
7 | public class LanguageSet {
8 | public Set c = new LinkedHashSet<>();
9 | public Set cpp = new LinkedHashSet<>();
10 | public Set objc = new LinkedHashSet<>();
11 | public Set objcpp = new LinkedHashSet<>();
12 |
13 | public void add(String value) {
14 | c.add(value);
15 | cpp.add(value);
16 | objc.add(value);
17 | objcpp.add(value);
18 | }
19 |
20 | public void addAll(List values) {
21 | for (String v : values) {
22 | add(v);
23 | }
24 | }
25 | public void addAll(LanguageSet set) {
26 | c.addAll(set.c);
27 | cpp.addAll(set.cpp);
28 | objc.addAll(set.objc);
29 | objcpp.addAll(set.objcpp);
30 | }
31 |
32 | public void remove(String value) {
33 | c.remove(value);
34 | cpp.remove(value);
35 | objc.remove(value);
36 | objcpp.remove(value);
37 | }
38 |
39 | @Override
40 | public String toString() {
41 | StringBuilder sb = new StringBuilder();
42 | sb.append("c: " + c);
43 | sb.append(" cpp: " + cpp);
44 | sb.append(" objc: " + objc);
45 | sb.append(" objcpp: " + objcpp);
46 | return sb.toString();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/cocoapods/MainPodfile.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.cocoapods;
2 |
3 | import java.io.File;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | public class MainPodfile {
8 | public List podnames = new ArrayList<>();
9 | public String platformMinVersion;
10 | public String platform;
11 | public File file;
12 |
13 | @Override
14 | public String toString() {
15 | StringBuilder sb = new StringBuilder();
16 | sb.append("file: " + file);
17 | sb.append("pod count: " + podnames.size() + "\n");
18 | sb.append("pod names: " + podnames + "\n");
19 | sb.append("platform: " + platform + "\n");
20 | sb.append("min version: " + platformMinVersion + "\n");
21 | return sb.toString();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/cocoapods/PlatformAndLanguageSet.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.cocoapods;
2 |
3 | import java.util.List;
4 |
5 | public class PlatformAndLanguageSet {
6 | public LanguageSet ios = new LanguageSet();
7 | public LanguageSet osx = new LanguageSet();
8 |
9 | public void addAll(PlatformAndLanguageSet v) {
10 | ios.addAll(v.ios);
11 | osx.addAll(v.osx);
12 | }
13 | public void addAll(List values) {
14 | for (String v : values) {
15 | ios.add(v);
16 | osx.add(v);
17 | }
18 | }
19 |
20 | public void remove(String value) {
21 | ios.remove(value);
22 | osx.remove(value);
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | StringBuilder sb = new StringBuilder();
28 | sb.append("ios: " + ios.toString());
29 | sb.append(" osx: " + osx.toString());
30 | return sb.toString();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/cocoapods/PlatformSet.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.cocoapods;
2 |
3 | import java.util.LinkedHashSet;
4 | import java.util.List;
5 | import java.util.Set;
6 |
7 | public class PlatformSet {
8 | public Set ios = new LinkedHashSet<>();
9 | public Set osx = new LinkedHashSet<>();
10 |
11 | public void addAll(PlatformSet v) {
12 | ios.addAll(v.ios);
13 | osx.addAll(v.osx);
14 | }
15 |
16 | public void addAll(List values) {
17 | for (String v : values) {
18 | ios.add(v);
19 | osx.add(v);
20 | }
21 | }
22 |
23 | public void add(String value) {
24 | ios.add(value);
25 | osx.add(value);
26 | }
27 |
28 | public Set get(String platform) {
29 | if (platform.contains("ios")) {
30 | return ios;
31 | }
32 | else if (platform.contains("osx")) {
33 | return osx;
34 | }
35 | return new LinkedHashSet();
36 | }
37 |
38 | @Override
39 | public String toString() {
40 | StringBuilder sb = new StringBuilder();
41 | sb.append("ios: " + ios.toString());
42 | sb.append(" osx: " + osx.toString());
43 | return sb.toString();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/cocoapods/PodUtils.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.cocoapods;
2 |
3 | import java.io.File;
4 | import java.nio.file.Path;
5 | import java.util.List;
6 |
7 | import com.defold.extender.ExtenderUtil;
8 |
9 | public class PodUtils {
10 |
11 | /*
12 | * @dir File Directory where files will be searched
13 | * @patter String Path pattern
14 | */
15 | static List listFilesGlob(File dir, String pattern) {
16 | String absPathPattern = Path.of(dir.getAbsolutePath(), pattern).toString();
17 | List files = ExtenderUtil.listFilesGlob(dir, absPathPattern);
18 | // Cocoapods uses Ruby where glob patterns are treated slightly differently:
19 | // Ruby: foo/**/*.h will find .h files in any subdirectory of foo AND in foo/
20 | // Java: foo/**/*.h will find .h files in any subdirectory of foo but NOT in foo/
21 | if (absPathPattern.contains("/**/")) {
22 | absPathPattern = absPathPattern.replaceFirst("\\/\\*\\*\\/", "/");
23 | files.addAll(ExtenderUtil.listFilesGlob(dir, absPathPattern));
24 | }
25 | return files;
26 | }
27 |
28 | static String sanitizePodName(String podName) {
29 | String sanitizedName = podName;
30 | sanitizedName = sanitizedName.replace("+", "\\+");
31 | return sanitizedName;
32 | }
33 |
34 | static String[] toPlistPlatforms(String[] platforms) {
35 | String[] result = new String[platforms.length];
36 | for (int idx = 0; idx < platforms.length; ++idx) {
37 | String platform = platforms[idx];
38 | if (platform.equals("arm64-ios")) {
39 | result[idx] = "iPhoneOS";
40 | } else if (platform.equals("x86_64-ios")) {
41 | result[idx] = "iPhoneSimulator";
42 | } else if (platform.contains("macos")) {
43 | result[idx] = "MacOSX";
44 | }
45 | }
46 | return result;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/data/DefoldSdk.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.data;
2 |
3 | import java.io.File;
4 | import java.util.concurrent.atomic.AtomicBoolean;
5 |
6 | import com.defold.extender.services.DefoldSdkService;
7 |
8 | public class DefoldSdk implements AutoCloseable {
9 | private File sdkDir;
10 | private String sdkHash;
11 | private DefoldSdkService sdkService;
12 | private boolean isVerified = false;
13 | private AtomicBoolean isUsed = new AtomicBoolean(true);
14 |
15 | public DefoldSdk(File sdkDir, String sdkHash, DefoldSdkService sdkService) {
16 | this.sdkDir = sdkDir;
17 | this.sdkHash = sdkHash;
18 | this.sdkService = sdkService;
19 |
20 | this.sdkService.acquireSdk(sdkHash);
21 | }
22 |
23 | public static DefoldSdk copyOf(DefoldSdk sdk) {
24 | DefoldSdk copy = new DefoldSdk(sdk.sdkDir, sdk.sdkHash, sdk.sdkService);
25 | copy.setVerified(sdk.isVerified);
26 | return copy;
27 | }
28 |
29 | public boolean isValid() {
30 | return this.isVerified && this.sdkDir != null && this.sdkDir.exists();
31 | }
32 |
33 | public File toFile() {
34 | return this.sdkDir;
35 | }
36 |
37 | public String getHash() {
38 | return this.sdkHash;
39 | }
40 |
41 | public void setVerified(boolean isValid) {
42 | this.isVerified = isValid;
43 | }
44 |
45 | public boolean isVerified() {
46 | return this.isVerified;
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return String.format("Sdk %s at %s", this.sdkHash, this.sdkDir.toString());
52 | }
53 |
54 | @Override
55 | public void close() {
56 | synchronized (this.isUsed) {
57 | if (this.isUsed.get()) {
58 | this.sdkService.releaseSdk(this.sdkHash);
59 | this.isUsed.set(false);
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/services/data/GCPInstanceState.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.data;
2 |
3 | public class GCPInstanceState {
4 | public volatile long lastTimeTouched = System.currentTimeMillis();
5 | }
6 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/tracing/ExtenderTracerInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.tracing;
2 |
3 | import io.micrometer.tracing.Tracer;
4 | import io.micrometer.tracing.propagation.Propagator;
5 | import org.apache.http.HttpException;
6 | import org.apache.http.HttpMessage;
7 | import org.apache.http.HttpRequest;
8 | import org.apache.http.HttpRequestInterceptor;
9 | import org.apache.http.protocol.HttpContext;
10 |
11 | import java.io.IOException;
12 |
13 |
14 | public class ExtenderTracerInterceptor implements HttpRequestInterceptor {
15 |
16 | private final Tracer tracer;
17 | private final Propagator propagator;
18 |
19 | public ExtenderTracerInterceptor(Tracer tracer, Propagator propagator) {
20 | this.tracer = tracer;
21 | this.propagator = propagator;
22 | }
23 |
24 | @Override
25 | public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
26 | if (tracer.currentSpan() != null) {
27 | propagator.inject(tracer.currentSpan().context(), request, HttpMessage::addHeader);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/server/src/main/java/com/defold/extender/tracing/TraceIdInResponseServletFilter.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.tracing;
2 |
3 | import io.micrometer.tracing.Span;
4 | import io.micrometer.tracing.Tracer;
5 | import jakarta.servlet.Filter;
6 | import jakarta.servlet.FilterChain;
7 | import jakarta.servlet.ServletException;
8 | import jakarta.servlet.ServletRequest;
9 | import jakarta.servlet.ServletResponse;
10 | import jakarta.servlet.http.HttpServletResponse;
11 |
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.stereotype.Component;
14 |
15 | import java.io.IOException;
16 |
17 | @Component
18 | public class TraceIdInResponseServletFilter implements Filter {
19 |
20 | public static final String TRACE_ID_HEADER_NAME = "X-TraceId";
21 |
22 | private final Tracer tracer;
23 |
24 | public TraceIdInResponseServletFilter(@Autowired Tracer tracer) {
25 | this.tracer = tracer;
26 | }
27 |
28 | @Override
29 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
30 | throws IOException, ServletException {
31 | final Span currentSpan = tracer.currentSpan();
32 | if (currentSpan != null) {
33 | HttpServletResponse resp = (HttpServletResponse) response;
34 | resp.addHeader(TRACE_ID_HEADER_NAME, currentSpan.context().traceId());
35 | }
36 | chain.doFilter(request, response);
37 | }
38 | }
--------------------------------------------------------------------------------
/server/src/main/resources/template.build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:{{gradle-plugin-version}}'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | }
17 |
18 | apply plugin: 'com.android.application'
19 |
20 | android {
21 | compileSdkVersion {{compile-sdk-version}}
22 |
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 |
28 | namespace = "com.defold.defold"
29 | }
30 |
31 | java {
32 | toolchain {
33 | languageVersion.set(JavaLanguageVersion.of(8))
34 | }
35 | }
36 |
37 | {{#gradle-files}}
38 | apply from: "{{{.}}}"
39 | {{/gradle-files}}
40 |
41 | dependencyLocking {
42 | lockAllConfigurations()
43 | lockFile = file("$projectDir/build/gradle.lockfile")
44 | }
45 |
46 | task downloadDependencies {
47 | doLast {
48 | project.configurations.releaseRuntimeClasspath.getResolvedConfiguration().getResolvedArtifacts().each {
49 | println "PATH: " + it.file + \
50 | " EXTENSION: " + it.extension + \
51 | " TYPE: " + it.type + \
52 | " MODULE_GROUP: " + it.moduleVersion.id.group + \
53 | " MODULE_NAME: " + it.moduleVersion.id.name + \
54 | " MODULE_VERSION: " + it.moduleVersion.id.version
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/server/src/main/resources/template.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0
4 | enable
5 | enable
6 | true
7 | static
8 | Library
9 | true
10 | false
11 | true
12 | True
13 |
14 |
15 | true
16 | false
17 |
18 |
19 | True
20 | True
21 |
22 | $(MSBuildProjectName)
23 | true
24 | false
25 | false
26 | {{BUILDDIR_CS}}
27 | {{BUILDDIR_CS}}
28 |
29 |
30 | {{#PINVOKE}}
31 |
32 | {{/PINVOKE}}
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/server/src/main/resources/template.gradle.properties:
--------------------------------------------------------------------------------
1 | # Gradle will use the Jetifier tool to migrate dependencies to Android X if android.enableJetifier is true
2 | android.enableJetifier={{android-enable-jetifier}}
3 |
4 | # Gradle will stop resolving dependencies if android.useAndroidX is false and a dependency is using Android X
5 | android.useAndroidX={{android-enable-jetifier}}
6 |
7 | org.gradle.java.home=/usr/local/jdk-21.0.5+11
8 |
--------------------------------------------------------------------------------
/server/src/main/resources/template.local.properties:
--------------------------------------------------------------------------------
1 | sdk.dir={{android-sdk-root}}
2 |
--------------------------------------------------------------------------------
/server/src/main/resources/template.modulemap:
--------------------------------------------------------------------------------
1 | {{FRAMEWORKOPT}} module {{MODULE_ID}} {
2 | umbrella header "{{UMBRELLA_HEADER}}"
3 | export *
4 | {{SUBMODULE}}
5 | }
6 |
7 | module {{MODULE_ID}}.Swift {
8 | {{#HEADERS}}
9 | header "{{{.}}}"
10 | {{/HEADERS}}
11 | export *
12 | requires objc
13 | }
14 |
--------------------------------------------------------------------------------
/server/src/main/resources/template.podfile:
--------------------------------------------------------------------------------
1 | platform :{{PLATFORM}}, '{{PLATFORM_VERSION}}'
2 | install! 'cocoapods', integrate_targets: false, skip_pods_project_generation: true
3 | use_frameworks!
4 | {{#PODS}}
5 | {{{.}}}
6 | {{/PODS}}
7 |
--------------------------------------------------------------------------------
/server/src/main/resources/template.umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 | {{#HEADERS}}
14 | #import "{{{.}}}"
15 | {{/HEADERS}}
16 |
17 | FOUNDATION_EXPORT double {{MODULE_ID}}VersionNumber;
18 | FOUNDATION_EXPORT const unsigned char {{MODULE_ID}}VersionString[];
19 |
20 |
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/TemplateExecutorTest.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.junit.jupiter.api.Test;
7 |
8 | import static org.assertj.core.api.Assertions.assertThat;
9 |
10 | public class TemplateExecutorTest {
11 |
12 | @Test
13 | public void templateVariablesShouldBeReplacedByContext() {
14 | TemplateExecutor templateExecutor = new TemplateExecutor();
15 | String template = "Hello {{name}}!";
16 | Map context = new HashMap<>();
17 | context.put("name", "James");
18 | String result = templateExecutor.execute(template, context);
19 | assertThat(result).isEqualTo("Hello James!");
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/TestUtils.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileReader;
6 | import java.io.IOException;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import com.defold.extender.cache.CacheEntry;
11 |
12 | public class TestUtils {
13 | public static final CacheEntry[] MOCK_CACHE_ENTRIES = {
14 | // Generated by "printf path/to/file1 | shasum -a256 -b"
15 | new CacheEntry("2f54e02440ff496294bec7192ade9d1aadc5ac2ce7f9e5642b2ade643a07df6c", "path/to/file1", true),
16 | new CacheEntry("bdac6b0b7f6f4a0c6f11c4713ff28dc29fa0f44c5c663a52833b2b562d67be34", "another/path/to/file2", true),
17 | new CacheEntry("75f51dedab2aa7c827f700e638b068483aff503114073efca509a1c88680a4ff", "last/path/for/file3", true)
18 | };
19 |
20 | public static final CacheEntry[] CACHE_ENTRIES = {
21 | new CacheEntry("2d8c2f6d978ca21712b5f6de36c9d31fa8e96a4fa5d8ff8b0188dfb9e7c171bb", "dir/test1.txt", true),
22 | new CacheEntry("7f3b61aeb34a8ea15c675ffddaa6af6a6fbdd031ed9786dcb2b35b351a132b31", "dir2/test2.txt", true)
23 | };
24 |
25 | public static Map envFileToMap(File inputFile) {
26 | Map result = new HashMap<>();
27 | try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) {
28 | String line = reader.readLine();
29 | while (line != null) {
30 | String[] splitted = line.split("=");
31 | result.put(splitted[0], splitted[1]);
32 | line = reader.readLine();
33 | }
34 | reader.close();
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | }
38 |
39 | return result;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/ZipUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 | import static org.junit.jupiter.api.Assertions.assertNotNull;
5 | import static org.junit.jupiter.api.Assertions.assertTrue;
6 |
7 | import java.io.File;
8 | import java.io.FileInputStream;
9 | import java.io.FileOutputStream;
10 | import java.io.IOException;
11 | import java.nio.file.Files;
12 | import java.nio.file.Path;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | import org.junit.jupiter.api.Test;
17 |
18 | public class ZipUtilsTest {
19 |
20 | @Test
21 | public void zipAndUnzipFiles() throws IOException {
22 | Path sourceFile1 = Files.createTempFile("zipTest", "tmp");
23 | Path sourceFile2 = Files.createTempFile("zipTest", "tmp");
24 | Path destinationFile = Files.createTempFile("archive", "zip");
25 | Path targetDirectory = Files.createTempDirectory("target");
26 |
27 | List files = new ArrayList<>();
28 | files.add(sourceFile1.toFile());
29 | files.add(sourceFile2.toFile());
30 |
31 | ZipUtils.zip(new FileOutputStream(destinationFile.toFile()), null, files);
32 |
33 | ZipUtils.unzip(new FileInputStream(destinationFile.toFile()), targetDirectory);
34 |
35 | assertEquals(2, targetDirectory.toFile().listFiles().length);
36 | }
37 |
38 | @Test
39 | public void zipFilesToFile() throws IOException {
40 | File targetDirectory = Files.createTempDirectory("test").toFile();
41 | String zipFilename = targetDirectory.getAbsolutePath() + "/archive.zip";
42 |
43 | Path sourceFile1 = Files.createTempFile("zipTest", "tmp");
44 | Path sourceFile2 = Files.createTempFile("zipTest", "tmp");
45 |
46 | List files = new ArrayList<>();
47 | files.add(sourceFile1.toFile());
48 | files.add(sourceFile2.toFile());
49 |
50 | File zipFile = ZipUtils.zip(files, null, zipFilename);
51 | File[] filesInTarget = targetDirectory.listFiles();
52 |
53 | assertNotNull(filesInTarget);
54 | assertEquals(1, filesInTarget.length);
55 | assertEquals(zipFile.length(), filesInTarget[0].length());
56 | assertTrue(filesInTarget[0].length() > 0);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/cache/CacheKeyGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | import com.defold.extender.TestUtils;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 |
7 | import java.io.File;
8 | import java.io.IOException;
9 | import java.net.URISyntaxException;
10 |
11 | import org.junit.jupiter.api.Test;
12 |
13 | public class CacheKeyGeneratorTest {
14 |
15 | private String generate(final String filename) throws URISyntaxException, IOException {
16 | CacheKeyGenerator cacheKeyGenerator = new CacheKeyGenerator();
17 | File file = new File(ClassLoader.getSystemResource(filename).toURI());
18 | return cacheKeyGenerator.generate(file);
19 | }
20 |
21 | @Test
22 | public void generateKeyForFile() throws IOException, URISyntaxException {
23 | assertEquals(TestUtils.CACHE_ENTRIES[0].getKey(), generate("upload/" + TestUtils.CACHE_ENTRIES[0].getPath()));
24 | }
25 |
26 | @Test
27 | public void generateKeyForAnotherFile() throws IOException, URISyntaxException {
28 | assertEquals(TestUtils.CACHE_ENTRIES[1].getKey(), generate("upload/" + TestUtils.CACHE_ENTRIES[1].getPath()));
29 | }
30 | }
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/cache/DummyDataCacheTest.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertFalse;
4 | import static org.junit.jupiter.api.Assertions.assertNull;
5 |
6 | import java.io.File;
7 |
8 | import org.junit.jupiter.api.BeforeEach;
9 | import org.junit.jupiter.api.Test;
10 |
11 | public class DummyDataCacheTest {
12 |
13 | private DataCache cache;
14 |
15 | @BeforeEach
16 | public void setUp() {
17 | cache = new DummyDataCache();
18 | }
19 |
20 | @Test
21 | public void makeSureCacheAlwaysReturnsNull() throws Exception {
22 | assertNull(cache.get("iAmNotHere"));
23 | assertFalse(cache.exists("iAmNotHere"));
24 |
25 | File file = new File(ClassLoader.getSystemResource("upload/dir/test1.txt").toURI());
26 | cache.put("iAmNotHere", file);
27 |
28 | assertNull(cache.get("iAmNotHere"));
29 | assertFalse(cache.exists("iAmNotHere"));
30 | }
31 | }
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/cache/info/CacheInfoFileParserTest.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache.info;
2 |
3 | import com.defold.extender.cache.CacheEntry;
4 | import com.defold.extender.TestUtils;
5 |
6 | import static org.junit.jupiter.api.Assertions.assertEquals;
7 |
8 | import java.io.File;
9 | import java.io.FileInputStream;
10 | import java.util.List;
11 |
12 | import org.junit.jupiter.api.Test;
13 |
14 | public class CacheInfoFileParserTest {
15 |
16 | @Test
17 | public void parseFile() throws Exception {
18 | CacheInfoFileParser parser = new CacheInfoFileParser();
19 | File file = new File(ClassLoader.getSystemResource("upload/ne-cache-info.json").toURI());
20 |
21 | CacheInfoWrapper info = parser.parse(file);
22 | List entries = info.getEntries();
23 |
24 | assertEquals(1, info.getVersion());
25 | assertEquals("sha256", info.getHashType());
26 | assertEquals(2, entries.size());
27 |
28 | CacheEntry entry1 = entries.get(0);
29 | assertEquals(TestUtils.CACHE_ENTRIES[0].getPath(), entry1.getPath());
30 | assertEquals(TestUtils.CACHE_ENTRIES[0].getKey(), entry1.getKey());
31 |
32 | CacheEntry entry2 = entries.get(1);
33 | assertEquals(TestUtils.CACHE_ENTRIES[1].getPath(), entry2.getPath());
34 | assertEquals(TestUtils.CACHE_ENTRIES[1].getKey(), entry2.getKey());
35 | }
36 |
37 | @Test
38 | public void parseInputStream() throws Exception {
39 | CacheInfoFileParser parser = new CacheInfoFileParser();
40 | File file = new File(ClassLoader.getSystemResource("upload/ne-cache-info.json").toURI());
41 |
42 | CacheInfoWrapper info = parser.parse(new FileInputStream(file));
43 | List entries = info.getEntries();
44 |
45 | assertEquals(1, info.getVersion());
46 | assertEquals("sha256", info.getHashType());
47 |
48 | CacheEntry entry1 = entries.get(0);
49 | assertEquals(TestUtils.CACHE_ENTRIES[0].getPath(), entry1.getPath());
50 | assertEquals(TestUtils.CACHE_ENTRIES[0].getKey(), entry1.getKey());
51 |
52 | CacheEntry entry2 = entries.get(1);
53 | assertEquals(TestUtils.CACHE_ENTRIES[1].getPath(), entry2.getPath());
54 | assertEquals(TestUtils.CACHE_ENTRIES[1].getKey(), entry2.getKey());
55 | }
56 |
57 | @Test
58 | public void parseOldFile() throws Exception {
59 | CacheInfoFileParser parser = new CacheInfoFileParser();
60 | File file = new File(ClassLoader.getSystemResource("upload/old-ne-cache-info.json").toURI());
61 |
62 | CacheInfoWrapper info = parser.parse(file);
63 | assertEquals(0, info.getVersion());
64 | assertEquals(null, info.getHashType());
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/cache/info/CacheInfoFileWriterTest.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.cache.info;
2 |
3 | import com.defold.extender.cache.CacheEntry;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 |
7 | import java.io.ByteArrayInputStream;
8 | import java.io.ByteArrayOutputStream;
9 | import java.util.Arrays;
10 |
11 | import org.junit.jupiter.api.Test;
12 |
13 | public class CacheInfoFileWriterTest {
14 |
15 | private static final CacheEntry[] ENTRIES = {
16 | new CacheEntry("675fef8ef8", "foo/bar.jar", true),
17 | new CacheEntry("234bc895fe73", "dir/asdf.zip", false)
18 | };
19 |
20 | private static final String JSON = "{\"version\":1,\"hashType\":\"sha256\",\"files\":[{\"key\":\"675fef8ef8\",\"path\":\"foo/bar.jar\",\"cached\":true}," +
21 | "{\"key\":\"234bc895fe73\",\"path\":\"dir/asdf.zip\",\"cached\":false}]}";
22 |
23 | @Test
24 | public void write() throws Exception {
25 | CacheInfoFileWriter cacheInfoFileWriter = new CacheInfoFileWriter();
26 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
27 |
28 | cacheInfoFileWriter.write(1, "sha256", Arrays.asList(ENTRIES), outputStream);
29 |
30 | ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
31 | byte[] buffer = new byte[inputStream.available()];
32 | inputStream.read(buffer, 0, inputStream.available());
33 |
34 | assertEquals(JSON, new String(buffer));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/server/src/test/java/com/defold/extender/services/cocoapods/PodUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.defold.extender.services.cocoapods;
2 |
3 | import static org.junit.jupiter.api.Assertions.assertEquals;
4 |
5 | import org.junit.jupiter.api.Test;
6 |
7 | public class PodUtilsTest {
8 |
9 | @Test
10 | public void testPodNameSanitize() {
11 | String[] testedNames = new String[] {
12 | "DivKit_LayoutKit",
13 | "GoogleUserMessagingPlatform",
14 | "Google-Mobile-Ads-SDK",
15 | "UIAlertController+Blocks",
16 | "Socket.IO-Client-Swift"
17 | };
18 | String[] expectedNames = new String[] {
19 | "DivKit_LayoutKit",
20 | "GoogleUserMessagingPlatform",
21 | "Google-Mobile-Ads-SDK",
22 | "UIAlertController\\+Blocks",
23 | "Socket.IO-Client-Swift"
24 | };
25 | assertEquals(testedNames.length, expectedNames.length);
26 | for (int idx = 0; idx < testedNames.length; ++idx) {
27 | assertEquals(expectedNames[idx], PodUtils.sanitizePodName(testedNames[idx]));
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/server/src/test/resources/junit-platform.properties:
--------------------------------------------------------------------------------
1 | junit.jupiter.execution.parallel.enabled = true
2 | junit.jupiter.execution.parallel.mode.default = concurrent
--------------------------------------------------------------------------------
/server/src/test/resources/upload/dir/test1.txt:
--------------------------------------------------------------------------------
1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
--------------------------------------------------------------------------------
/server/src/test/resources/upload/dir2/test2.txt:
--------------------------------------------------------------------------------
1 | A merry little surge of electricity piped by automatic alarm from the mood organ beside his
2 | bed awakened Rick Deckard. Surprised - it always surprised him to find himself awake
3 | without prior notice - he rose from the bed, stood up in his multicolored pajamas, and
4 | stretched.
--------------------------------------------------------------------------------
/server/src/test/resources/upload/ne-cache-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "hashType": "sha256",
4 | "files": [
5 | {
6 | "path": "dir/test1.txt",
7 | "key": "2d8c2f6d978ca21712b5f6de36c9d31fa8e96a4fa5d8ff8b0188dfb9e7c171bb",
8 | "cached": true
9 | },
10 | {
11 | "path": "dir2/test2.txt",
12 | "key": "7f3b61aeb34a8ea15c675ffddaa6af6a6fbdd031ed9786dcb2b35b351a132b31",
13 | "cached": true
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/server/src/test/resources/upload/old-ne-cache-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | {
4 | "path": "dir/test1.txt",
5 | "key": "2d8c2f6d978ca21712b5f6de36c9d31fa8e96a4fa5d8ff8b0188dfb9e7c171bb",
6 | "cached": true
7 | },
8 | {
9 | "path": "dir2/test2.txt",
10 | "key": "7f3b61aeb34a8ea15c675ffddaa6af6a6fbdd031ed9786dcb2b35b351a132b31",
11 | "cached": true
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/server/test-data/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/server/test-data/_app/rjava/com/dummy/R.java:
--------------------------------------------------------------------------------
1 | package com.dummy;
2 |
3 | public final class R {
4 | public static final class raw {
5 | public static final int apa=0x7f040000;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/server/test-data/alib/alib.cpp:
--------------------------------------------------------------------------------
1 |
2 | int alib_mul(int x, int y)
3 | {
4 | return x * y;
5 | }
6 |
--------------------------------------------------------------------------------
/server/test-data/blib/blib.cpp:
--------------------------------------------------------------------------------
1 |
2 | extern int alib_mul(int x, int y);
3 |
4 | int blib_add(int x, int y)
5 | {
6 | return x + y;
7 | }
8 |
9 | int blib_mul(int x, int y)
10 | {
11 | return alib_mul(x, y);
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/server/test-data/blib/blib.h:
--------------------------------------------------------------------------------
1 | #ifndef BLIB_H
2 | #endif BLIB_H
3 |
4 | int blib_mul(int x, int y);
5 |
6 | #endif // BLIB_H
--------------------------------------------------------------------------------
/server/test-data/build-libs.sh:
--------------------------------------------------------------------------------
1 |
2 | # How to build on Windows
3 | # * Make a shared directory between OSX/Win32 for test-data in Parallels Desktop
4 | # * On the windows machine, open an msys terminal
5 | # * Change directory to the new drive (E.g. X: "cd x:") which was mounted for you by Parallels Desktop
6 | # * Run "sh build-libs.sh"
7 | # All files should be named and copied automatically by this script
8 |
9 |
10 | source ./compile.sh
11 |
12 | # Find all .cpp files in a folder and make a lib of each of them
13 | function CompileLibsToExtension {
14 | local dir=$1
15 | local extension=$2
16 | local lib_type=$3
17 |
18 | for file in $dir/*.cpp
19 | do
20 | local name=$(basename $file)
21 | name="${name%.*}"
22 | echo $name $file $lib_type
23 | if [ "$lib_type" == "dynamic" ]; then
24 | CompileDynamic $name $file $extension
25 | else
26 | Compile $name $file $extension
27 | fi
28 | done
29 | }
30 |
31 | function Copy {
32 | mkdir -p $(dirname test-data/$2)
33 | cp -v test-data/$1 test-data/$2
34 | }
35 |
36 |
37 | CompileLibsToExtension enginelibs engineext/lib static
38 |
39 | # copy these into the "a" sdk
40 | mkdir -p sdk/a/defoldsdk/lib
41 | cp -v -r engineext/lib sdk/a/defoldsdk/
42 | rm -rf ./engineext
43 |
44 | # The sdk's has different naming
45 | mv sdk/a/defoldsdk/lib/x86_64-osx sdk/a/defoldsdk/lib/x86_64-macos
46 |
47 | # Need these folders as well (empty is fine)
48 | mkdir -p sdk/a/defoldsdk/ext/lib/darwin
49 | mkdir -p sdk/a/defoldsdk/ext/lib/x86_64-macos
50 |
51 | CompileLibsToExtension alib ext/lib static
52 | CompileLibsToExtension alib ext2/lib static
53 | CompileLibsToExtension blib ext2/lib static
54 | CompileLibsToExtension stdlib ext_std/lib static
55 |
56 | CompileLibsToExtension dynamic_specific1 ext_dyn_libs/lib dynamic
57 | CompileLibsToExtension dynamic_specific2 ext_dyn_libs2/lib dynamic
58 |
59 | (cd testproject_appmanifest && ./build.sh)
60 |
61 |
--------------------------------------------------------------------------------
/server/test-data/checksum_sdk/create_sdk.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | zip -r test_sdk.zip defoldsdk/
3 | printf '%s' "$(sha256 -q ./test_sdk.zip)" > test_sdk.sha256
4 | printf '%s' "$(sha256 -q ./defoldsdk/sdk_file_1.yml)" > test_sdk_invalid.sha256
5 |
--------------------------------------------------------------------------------
/server/test-data/checksum_sdk/defoldsdk/test_header.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include