99 std::string sdkRoot,
bool debug) {
101 namespace fs = std::filesystem;
103 const Platform
p = parsePlatform(platform);
104 if (platform.empty()) {
105 cerr << rang::fg::red <<
"eve build: missing platform. Pass a platform name, "
106 <<
"e.g. `eve build android`." << rang::fg::reset << endl;
109 if (
p == Platform::Unknown) {
110 cerr << rang::fg::red <<
"eve build: unknown platform '" << platform
111 <<
"' (supported: android)" << rang::fg::reset << endl;
114 if (
p != Platform::Android) {
115 cerr << rang::fg::red <<
"eve build: SDK mode currently supports 'android' only; "
116 <<
"desktop targets are packaged with `eve package`."
117 << rang::fg::reset << endl;
124 const std::string asdk = androidSdkRoot();
126 if (!fs::is_directory(asdk, ec)) {
127 cerr << rang::fg::red <<
"Android SDK not found at " << asdk <<
". Run `eve get android` "
128 <<
"first, or set ANDROID_HOME / EVENGINE_ANDROID_SDK."
129 << rang::fg::reset << endl;
132 applyEnvFile((fs::path(asdk) /
"eve-android.env").
string());
136 std::string
root = findSdkRoot(sdkRoot);
138 cerr <<
"eve build: cannot locate the EVEngine SDK. Pass --sdk <dir>, set "
139 "$EVENGINE_SDK, or run `eve get android` first."
143 const std::string sdkPlat = sdkTargetPlatform(
root);
144 if (sdkPlat !=
"android") {
145 cerr << rang::fg::red <<
"eve build: this SDK targets '" << sdkPlat
146 <<
"', not android. Get the android SDK." << rang::fg::reset << endl;
151 const std::string javaHome = getEnv(
"JAVA_HOME");
152 const std::string gradleHome = getEnv(
"GRADLE_HOME");
153 if (javaHome.empty() || gradleHome.empty() ||
154 !fs::is_regular_file(gradleLauncher(gradleHome), ec)) {
155 cerr << rang::fg::red <<
"Missing JDK/Gradle for Android builds. Run `eve get android` "
156 <<
"to install them (JAVA_HOME / GRADLE_HOME)." << rang::fg::reset << endl;
159 setEnv(
"ANDROID_HOME", asdk);
160 setEnv(
"ANDROID_SDK_ROOT", asdk);
163 std::string game =
path;
164 if (game.empty() || game ==
".") game = fs::absolute(
".").string();
165 else game = fs::absolute(game, ec).string();
166 if (ec || !fs::is_directory(game, ec)) {
167 cerr << rang::fg::red <<
"eve build: bad game path '" <<
path <<
"'"
168 << rang::fg::reset << endl;
171 if (fs::equivalent(game,
root, ec) || fs::path(game) == fs::path(
root)) {
172 const auto demo = fs::path(
root) /
"platform" /
"game-shell";
173 if (fs::is_directory(demo, ec)) game = demo.string();
177 const std::string outDir =
output.empty()
178 ? (fs::current_path() /
"build" /
"eve-android").
string()
179 : fs::absolute(
output, ec).string();
180 fs::remove_all(outDir, ec);
181 fs::create_directories(outDir, ec);
182 const std::string apkDir = (fs::path(outDir) /
"apk").
string();
186 if (!copyTree((fs::path(
root) /
"platform" /
"apk").
string(), apkDir,
187 {
"build",
".gradle",
"local.properties",
"app/build"})) {
188 cerr << rang::fg::red <<
"SDK is missing the android APK template (platform/apk)."
189 << rang::fg::reset << endl;
192 const std::string assetsGame = (fs::path(apkDir) /
"app" /
"src" /
"main" /
"assets" /
"game").
string();
193 if (!copyTreeContents(game, assetsGame)) {
194 cerr << rang::fg::red <<
"Failed to copy game assets." << rang::fg::reset << endl;
197 const std::string jniDir = (fs::path(apkDir) /
"app" /
"src" /
"main" /
"jniLibs" /
"arm64-v8a").
string();
198 fs::create_directories(jniDir, ec);
200 for (
const auto& entry : fs::directory_iterator(fs::path(
root) /
"lib", ec)) {
201 if (entry.is_regular_file() && entry.path().extension() ==
".so") {
202 fs::copy_file(entry.path(), fs::path(jniDir) / entry.path().filename(),
203 fs::copy_options::overwrite_existing, ec);
208 cerr << rang::fg::yellow
209 <<
"note: no prebuilt .so found in the SDK lib/ dir; the APK will be "
210 "missing native code."
211 << rang::fg::reset << endl;
215 std::string sdkProp = asdk;
216 for (
char&
c : sdkProp)
217 if (
c ==
'\\')
c =
'/';
218 std::ofstream
f(fs::path(apkDir) /
"local.properties", std::ios::binary | std::ios::trunc);
219 f <<
"sdk.dir=" << sdkProp <<
"\n";
223 const std::string cmd = gradleCommand(apkDir, gradleHome, debug);
224 cout <<
"eve build: " << cmd << endl;
225 const int rc = runShell(cmd);
227 cerr << rang::fg::red <<
"eve build: gradle failed (exit " << rc <<
")."
228 << rang::fg::reset << endl;
233 const std::string
variant = debug ?
"debug" :
"release";
236 std::vector<std::string> names = {
"app-" +
variant +
".apk"};
237 if (!debug) names.push_back(
"app-release-unsigned.apk");
238 for (
const auto&
n : names) {
239 const fs::path candidate = fs::path(apkDir) /
"app" /
"build" /
"outputs" /
"apk" /
241 if (fs::is_regular_file(candidate, ec)) {
248 cerr << rang::fg::yellow
249 <<
"note: gradle finished but no APK found under "
250 << (fs::path(apkDir) /
"app" /
"build" /
"outputs" /
"apk").
string()
251 << rang::fg::reset << endl;
254 cout << rang::fg::green <<
"Built APK -> " << rang::fg::reset << apk.string();
255 if (apk.filename() ==
"app-release-unsigned.apk")
256 cout <<
" (unsigned; add a signing config to release properly)";