Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion commandLine/src/defines.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#define OFPROJECTGENERATOR_MAJOR_VERSION "0"
#define OFPROJECTGENERATOR_MINOR_VERSION "111"
#define OFPROJECTGENERATOR_MINOR_VERSION "112"
#define OFPROJECTGENERATOR_PATCH_VERSION "0"

#define PG_VERSION (OFPROJECTGENERATOR_MAJOR_VERSION "." OFPROJECTGENERATOR_MINOR_VERSION "." OFPROJECTGENERATOR_PATCH_VERSION)
Expand Down
28 changes: 27 additions & 1 deletion commandLine/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "Utils.h"
#include "defines.h"
#include <regex>
#include "ofUtils.h"
#include "ofFileUtils.h"
#include "ofSystemUtils.h"
Expand Down Expand Up @@ -35,7 +36,9 @@ enum optionIndex { UNKNOWN,
DEFINES,
POSTBUILD,
MSYS2ENV,
EMSCRIPTEN_WASM
EMSCRIPTEN_WASM,
APP_VERSION,
APP_BUILD
};

constexpr option::Descriptor usage[] = {
Expand Down Expand Up @@ -67,6 +70,8 @@ constexpr option::Descriptor usage[] = {
{ MSYS2ENV, 0, "k", "msys2env", option::Arg::Optional, " --msys2env, -k \tMSYS2 environment for the msys2 / VSCode targets: ucrt64 (default) | mingw64 | clang64 | clangarm64" },
{ EMSCRIPTEN_WASM, 0, "W", "emscripten-wasm", option::Arg::Optional, " --emscripten-wasm, -W \temscripten WebAssembly: 32 (default) | 64 (wasm64, MEMORY64), written to config.make PROJECT_EMSCRIPTEN_WASM" },

{ APP_VERSION, 0, "", "app-version", option::Arg::Optional, " --app-version \tapp version, eg 1.2.3 (default 1.0): config.make / CMake APP_VERSION, Xcode MARKETING_VERSION, Android versionName" },
{ APP_BUILD, 0, "", "app-build", option::Arg::Optional, " --app-build \tbuild number, eg 42 (default 1): config.make / CMake APP_BUILD, Xcode CURRENT_PROJECT_VERSION, Android versionCode" },
{ 0, 0, 0, 0, 0, 0 }
};

Expand Down Expand Up @@ -96,6 +101,8 @@ vector<string> frameworks;
vector<string> defines;
string postBuildScript;
string emscriptenWasm; // empty keeps the project's config.make value
string appVersion; // empty keeps the project's value
string appBuild;
string ofPathEnv;
string templateName;
string msys2Env = "ucrt64";
Expand Down Expand Up @@ -268,6 +275,7 @@ void updateProject(const fs::path & path, const string & target, bool bConsiderP
auto project = getTargetProject(target);
project->setMsys2Environment(msys2Env);
project->setEmscriptenWasm(emscriptenWasm);
project->setAppVersion(appVersion, appBuild);
project->create(path, templateName);

if (bConsiderParameterAddons && bAddonsPassedIn) {
Expand Down Expand Up @@ -628,6 +636,23 @@ int pgMain(int argc, char ** argv) {
}
}

if (options[APP_VERSION].count() > 0 && options[APP_VERSION].arg != NULL) {
std::string v = ofTrim(options[APP_VERSION].arg);
if (std::regex_match(v, std::regex(R"(\d+(\.\d+)*)"))) {
appVersion = v;
} else {
ofLogWarning() << "app version " << v << " should be digits and dots (eg 1.2.3) - keeping the project's version";
}
}
if (options[APP_BUILD].count() > 0 && options[APP_BUILD].arg != NULL) {
std::string b = ofTrim(options[APP_BUILD].arg);
if (std::regex_match(b, std::regex(R"([1-9]\d*)"))) {
appBuild = b;
} else {
ofLogWarning() << "app build " << b << " should be a positive integer - keeping the project's build";
}
}

if (options[POSTBUILD].count() > 0 && options[POSTBUILD].arg != NULL) {
postBuildScript = ofTrim(options[POSTBUILD].arg);
}
Expand Down Expand Up @@ -773,6 +798,7 @@ int pgMain(int argc, char ** argv) {
auto project = getTargetProject(t);
project->setMsys2Environment(msys2Env);
project->setEmscriptenWasm(emscriptenWasm);
project->setAppVersion(appVersion, appBuild);
project->create(projectPath, templateName);
if(bAddonsPassedIn){
for (auto & addon : addons) {
Expand Down
153 changes: 153 additions & 0 deletions commandLine/src/projects/baseProject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,65 @@ static std::string readCMakeEmscriptenWasm(const fs::path & cmakeLists) {
return "";
}

// APP_VERSION / APP_BUILD in config.make (openFrameworks#8592)
static std::pair<std::string, std::string> readMakeAppVersion(const fs::path & configMake) {
std::string version, build;
if (!fs::exists(configMake)) return { version, build };
for (auto & line : fileToStrings(configMake)) {
auto config = ofTrim(line);
if (config.empty() || config[0] == '#') continue;
auto eq = config.find('=');
if (eq == std::string::npos) continue;
auto var = ofTrim(config.substr(0, eq));
if (!var.empty() && (var.back() == '?' || var.back() == '+' || var.back() == ':')) var = ofTrim(var.substr(0, var.size() - 1));
if (var == "APP_VERSION") version = ofTrim(config.substr(eq + 1));
if (var == "APP_BUILD") build = ofTrim(config.substr(eq + 1));
}
return { version, build };
}

// set(APP_VERSION ...) / set(APP_BUILD ...) in an openFrameworks CMakeLists.txt (openFrameworks#8586)
static std::pair<std::string, std::string> readCMakeAppVersion(const fs::path & cmakeLists) {
static const std::regex versionSet(R"re(^\s*set\(\s*APP_VERSION\s+"?([0-9.]+)"?\s*\))re");
static const std::regex buildSet(R"re(^\s*set\(\s*APP_BUILD\s+"?([0-9]+)"?\s*\))re");
std::string version, build;
std::smatch m;
if (!fs::exists(cmakeLists)) return { version, build };
for (auto & line : fileToStrings(cmakeLists)) {
if (std::regex_search(line, m, versionSet)) version = m[1];
if (std::regex_search(line, m, buildSet)) build = m[1];
}
return { version, build };
}

// MARKETING_VERSION / CURRENT_PROJECT_VERSION in the .xcodeproj target settings
static std::pair<std::string, std::string> readXcodeAppVersion(const fs::path & pbxproj) {
static const std::regex versionSet(R"re("?MARKETING_VERSION"?\s*[:=]\s*"?([0-9.]+)"?)re");
static const std::regex buildSet(R"re("?CURRENT_PROJECT_VERSION"?\s*[:=]\s*"?([0-9]+)"?)re");
std::string version, build;
std::smatch m;
if (!fs::exists(pbxproj)) return { version, build };
for (auto & line : fileToStrings(pbxproj)) {
if (version.empty() && std::regex_search(line, m, versionSet)) version = m[1];
if (build.empty() && std::regex_search(line, m, buildSet)) build = m[1];
}
return { version, build };
}

// versionName / versionCode in the android ofApp/build.gradle
static std::pair<std::string, std::string> readGradleAppVersion(const fs::path & gradle) {
static const std::regex versionSet(R"re(^\s*versionName\s+['"]([0-9.]+)['"])re");
static const std::regex buildSet(R"re(^\s*versionCode\s+([0-9]+))re");
std::string version, build;
std::smatch m;
if (!fs::exists(gradle)) return { version, build };
for (auto & line : fileToStrings(gradle)) {
if (std::regex_search(line, m, versionSet)) version = m[1];
if (std::regex_search(line, m, buildSet)) build = m[1];
}
return { version, build };
}

bool baseProject::create(const fs::path & _path, string templateName){
// alert("baseProject::create " + path.string() + " : " + templateName, 35);
auto path = _path; // just because it is const
Expand Down Expand Up @@ -192,6 +251,32 @@ bool baseProject::create(const fs::path & _path, string templateName){
if (!emscriptenWasm.empty()) {
ofLogNotice() << "{ \"emscriptenWasm\": \"" << emscriptenWasm << "\" }";
}

// app version / build: config.make first, then CMakeLists, Xcode, Android; the CLI value wins
{
std::vector<std::pair<std::string, std::pair<std::string, std::string>>> found {
{ "config.make", readMakeAppVersion(projectDir / "config.make") },
{ "CMakeLists.txt", readCMakeAppVersion(projectDir / "CMakeLists.txt") },
{ "xcodeproj", readXcodeAppVersion(projectDir / (projectName + ".xcodeproj") / "project.pbxproj") },
{ "build.gradle", readGradleAppVersion(projectDir / "ofApp" / "build.gradle") },
};
std::string version, build, versionFrom, buildFrom;
for (auto & f : found) {
if (version.empty() && !f.second.first.empty()) { version = f.second.first; versionFrom = f.first; }
if (build.empty() && !f.second.second.empty()) { build = f.second.second; buildFrom = f.first; }
}
for (auto & f : found) {
if (!f.second.first.empty() && f.second.first != version) {
ofLogWarning() << "app version " << f.second.first << " in " << f.first << " differs from " << version << " in " << versionFrom << " - using " << versionFrom;
}
if (!f.second.second.empty() && f.second.second != build) {
ofLogWarning() << "app build " << f.second.second << " in " << f.first << " differs from " << build << " in " << buildFrom << " - using " << buildFrom;
}
}
appVersion = !appVersionOption.empty() ? appVersionOption : (!version.empty() ? version : "1.0");
appBuild = !appBuildOption.empty() ? appBuildOption : (!build.empty() ? build : "1");
ofLogNotice() << "{ \"appVersion\": \"" << appVersion << "\", \"appBuild\": \"" << appBuild << "\" }";
}
if (emscriptenWasm == "64") {
bool bHasWasm64Libs = false;
if (fs::exists(getOFRoot() / "libs")) {
Expand Down Expand Up @@ -338,6 +423,8 @@ bool baseProject::save(){
std::ofstream saveConfig(projectDir / "config.make");
bool bWroteDefines = false;
bool bWroteWasm = false;
bool bWroteAppVersion = false;
bool bWroteAppBuild = false;

for (auto & str : lines) {
//add the of root path
Expand Down Expand Up @@ -370,6 +457,19 @@ bool baseProject::save(){
bWroteWasm = true;
}
}
// app version / build (openFrameworks#8592): the defaults leave the section commented
else if( str.rfind("# APP_VERSION =", 0) == 0 || str.rfind("APP_VERSION =", 0) == 0 ){
if (!bWroteAppVersion) {
saveConfig << (isDefaultAppVersion() ? "# " : "") << "APP_VERSION = " << appVersion << std::endl;
bWroteAppVersion = true;
}
}
else if( str.rfind("# APP_BUILD =", 0) == 0 || str.rfind("APP_BUILD =", 0) == 0 ){
if (!bWroteAppBuild) {
saveConfig << (isDefaultAppVersion() ? "# " : "") << "APP_BUILD = " << appBuild << std::endl;
bWroteAppBuild = true;
}
}
else if( emscriptenWasm == "32" && str.rfind("EMSCRIPTEN_MEMORY64", 0) == 0 ){
saveConfig << "# " << str << std::endl;
} else {
Expand All @@ -383,7 +483,14 @@ bool baseProject::save(){
saveConfig << "# emscripten: 32 (default) or 64 (wasm64, MEMORY64)" << std::endl;
saveConfig << "PROJECT_EMSCRIPTEN_WASM = 64" << std::endl;
}
if (!isDefaultAppVersion() && lines.size() && (!bWroteAppVersion || !bWroteAppBuild)) {
saveConfig << std::endl << "# app version and build number" << std::endl;
if (!bWroteAppVersion) saveConfig << "APP_VERSION = " << appVersion << std::endl;
if (!bWroteAppBuild) saveConfig << "APP_BUILD = " << appBuild << std::endl;
}
saveConfig.close();
saveCMakeLists();
saveAppVersionFiles();

#ifdef OFADDON_OUTPUT_JSON_DEBUG
saveAddonsToJson();
Expand Down Expand Up @@ -980,6 +1087,34 @@ bool baseProject::copyTemplateFile::run() {
return true;
}

// app version / build in Project.xcconfig (Xcode) and ofApp/build.gradle (Android)
static void replaceLines(const fs::path & file, const std::vector<std::pair<std::regex, std::string>> & replacements) {
if (!fs::exists(file)) return;
auto lines = fileToStrings(file);
std::ofstream out(file);
for (auto & line : lines) {
// regex group 1 is kept, the rest of the match becomes the value ("$1" + "42" would read as group 14)
for (auto & r : replacements) {
std::smatch m;
if (std::regex_search(line, m, r.first)) {
line = m.prefix().str() + m[1].str() + r.second + m.suffix().str();
}
}
out << line << std::endl;
}
}

void baseProject::saveAppVersionFiles() {
replaceLines(projectDir / "Project.xcconfig", {
{ std::regex(R"re(^(\s*MARKETING_VERSION\s*=\s*)[^\s/]+)re"), appVersion },
{ std::regex(R"re(^(\s*CURRENT_PROJECT_VERSION\s*=\s*)[^\s/]+)re"), appBuild },
});
replaceLines(projectDir / "ofApp" / "build.gradle", {
{ std::regex(R"re(^(\s*versionCode\s+)[0-9]+)re"), appBuild },
{ std::regex(R"re(^(\s*versionName\s+)['"][^'"]*['"])re"), "'" + appVersion + "'" },
});
}

// OF_ROOT and PROJECT_EMSCRIPTEN_WASM in an openFrameworks app CMakeLists.txt, like config.make
void baseProject::saveCMakeLists() {
fs::path cmakeLists { projectDir / "CMakeLists.txt" };
Expand All @@ -995,9 +1130,27 @@ void baseProject::saveCMakeLists() {
}
static const std::regex ofRootSet(R"re(^(\s*)set\(OF_ROOT\s+"[^"]*"\))re");
static const std::regex wasmSet(R"re(^\s*set\(\s*PROJECT_EMSCRIPTEN_WASM\b)re");
static const std::regex appVersionSet(R"re(^\s*#?\s*set\(\s*APP_(VERSION|BUILD)\b)re");
static const std::regex addApp(R"re(^\s*of_add_app\s*\()re");
string comment = isDefaultAppVersion() ? "# " : "";
bool bWroteAppVersion = false;
auto writeAppVersion = [&](std::ofstream & o) {
if (bWroteAppVersion) return;
o << comment << "set(APP_VERSION " << appVersion << ")" << std::endl;
o << comment << "set(APP_BUILD " << appBuild << ")" << std::endl;
bWroteAppVersion = true;
};

std::ofstream out(cmakeLists);
for (auto & line : lines) {
if (std::regex_search(line, appVersionSet)) {
writeAppVersion(out);
continue;
}
if (std::regex_search(line, addApp) && !bWroteAppVersion) {
if (!isDefaultAppVersion()) writeAppVersion(out);
bWroteAppVersion = true;
}
if (!emscriptenWasm.empty() && std::regex_search(line, wasmSet)) continue;
if (line.find(OF_CMAKE_INCLUDE) != std::string::npos && emscriptenWasm == "64") {
out << "# emscripten: 32 (default) or 64 (wasm64, MEMORY64)" << std::endl;
Expand Down
5 changes: 5 additions & 0 deletions commandLine/src/projects/baseProject.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class baseProject {
virtual void setMsys2Environment(const std::string & env) {}
// emscripten wasm32 / wasm64 (-W/--emscripten-wasm), written to config.make PROJECT_EMSCRIPTEN_WASM; empty keeps the project's value
void setEmscriptenWasm(const std::string & bits) { emscriptenWasmOption = bits; }
void setAppVersion(const std::string & version, const std::string & build) { appVersionOption = version; appBuildOption = build; }


#ifdef OFADDON_OUTPUT_JSON_DEBUG
Expand Down Expand Up @@ -186,6 +187,10 @@ class baseProject {
std::string emscriptenWasmOption; // from the CLI, empty = keep
std::string emscriptenWasm; // value for this project: "", "32" or "64"
void saveCMakeLists();
void saveAppVersionFiles();
std::string appVersionOption, appBuildOption; // from the CLI, empty = keep
std::string appVersion { "1.0" }, appBuild { "1" }; // value for this project
bool isDefaultAppVersion() const { return appVersion == "1.0" && appBuild == "1"; }

//cached addons - if an addon is requested more than once, avoid loading from disk as it's quite slow
std::map<std::string,std::map<std::string, ofAddon>> addonsCache; //indexed by [platform][supplied path]
Expand Down
17 changes: 17 additions & 0 deletions commandLine/src/projects/xcodeProject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,23 @@ bool xcodeProject::saveProjectFile() {
}
}

// app version / build on every target configuration (target settings override Project.xcconfig)
try {
auto & objects = j["objects"];
for (auto & target : objects) {
if (!target.is_object() || target.value("isa", "") != "PBXNativeTarget" || !target.contains("buildConfigurationList")) continue;
auto & configList = objects[target["buildConfigurationList"].get<string>()];
if (!configList.contains("buildConfigurations")) continue;
for (auto & configUUID : configList["buildConfigurations"]) {
auto & config = objects[configUUID.get<string>()];
config["buildSettings"]["MARKETING_VERSION"] = appVersion;
config["buildSettings"]["CURRENT_PROJECT_VERSION"] = appBuild;
}
}
} catch (std::exception & e) {
ofLogError(xcodeProject::LOG_NAME) << "app version: " << e.what();
}

std::ofstream jsonFile(fileName);

// This is not pretty but address some differences in nlohmann json 3.11.2 to 3.11.3
Expand Down
25 changes: 23 additions & 2 deletions frontend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,21 @@ ipcRenderer.on('setGenerateMode', (event, arg) => {
if (arg === 'createMode') {
$('#wasmDropdown').dropdown('set selected', '32');
$('#wasmNote').hide();
$('#appVersion').val('');
$('#appBuild').val('');
$('#appVersionNote').hide();
}
});

//-------------------------------------------
// existing project's app version / build, so updating keeps them
ipcRenderer.on('setAppVersion', (event, { version, build, differs }) => {
$('#appVersion').val(version || '');
$('#appBuild').val(build || '');
const note = (differs || []).map((d) => t('note.appVersionDiffers', { file: d.file, value: d.value })).join(' ');
$('#appVersionNote').text(note).toggle(!!defaultSettings.advancedMode && note.length > 0);
});

//-------------------------------------------
// existing project's PROJECT_EMSCRIPTEN_WASM, so updating keeps it
ipcRenderer.on('setEmscriptenWasm', (event, bits) => {
Expand Down Expand Up @@ -1240,7 +1252,9 @@ function generate() {
defines: definesArr.join(','),
postBuild: $('#postBuildScript').val().trim(),
msys2Env: $('#msys2EnvField').css('display') !== 'none' ? $('#msys2EnvDropdown').dropdown('get value') : '',
emscriptenWasm: $('#wasmField').css('display') !== 'none' ? $('#wasmDropdown').dropdown('get value') : ''
emscriptenWasm: $('#wasmField').css('display') !== 'none' ? $('#wasmDropdown').dropdown('get value') : '',
appVersion: $('#appVersion').val().trim(),
appBuild: $('#appBuild').val().trim()
};

// console.log(gen);
Expand All @@ -1250,6 +1264,10 @@ function generate() {
$("#projectPath").oneTimeTooltip(t('tooltip.projectPathEmpty'));
} else if (gen.platformList == null || lengthOfPlatforms == 0) {
$("#platformsDropdown").oneTimeTooltip(t('tooltip.selectPlatformFirst'));
} else if (gen.appVersion !== '' && !/^\d+(\.\d+)*$/.test(gen.appVersion)) {
$("#appVersion").oneTimeTooltip(t('tooltip.appVersionInvalid'));
} else if (gen.appBuild !== '' && !/^[1-9]\d*$/.test(gen.appBuild)) {
$("#appBuild").oneTimeTooltip(t('tooltip.appBuildInvalid'));
} else {
// the template pick is cleared in the #projectName change handler instead,
// as soon as the user points at a different project - not here, so a plain
Expand Down Expand Up @@ -1415,8 +1433,11 @@ function updateRendererFieldVisibility() {
$('#webglField').toggle(!!defaultSettings.advancedMode && emscripten);
$('#wasmField').toggle(!!defaultSettings.advancedMode && emscripten);
$('#glesField').toggle(!!defaultSettings.advancedMode && selectedPlatforms.some((p) => ['ios', 'macos'].includes(p)));
// advanced only; hidden fields keep the project's values, so updating still keeps them
$('#appVersionField').css('display', defaultSettings.advancedMode ? 'flex' : 'none');
$('#appVersionNote').toggle(!!defaultSettings.advancedMode && $('#appVersionNote').text().length > 0);

const glPlatforms = ['osx', 'vs', 'msys2', 'linux', 'linux64', 'linuxarmv6l', 'linuxaarch64', 'vscode'];
const glPlatforms = ['osx', 'vs', 'msys2', 'linux', 'linux64', 'linuxarmv6l', 'linuxarmv7l', 'linuxaarch64', 'vscode'];
$('#glField').toggle(!!defaultSettings.advancedMode && selectedPlatforms.some((p) => glPlatforms.includes(p)));
// Apple's OpenGL stops at 4.1
const maxGL = selectedPlatforms.includes('osx') ? 4.1 : 4.6;
Expand Down
Loading
Loading