diff --git a/commandLine/commandLine.xcodeproj/project.pbxproj b/commandLine/commandLine.xcodeproj/project.pbxproj index 59e911d9..b6fa9cd7 100644 --- a/commandLine/commandLine.xcodeproj/project.pbxproj +++ b/commandLine/commandLine.xcodeproj/project.pbxproj @@ -235,8 +235,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "cp \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME\" \"$TARGET_BUILD_DIR/projectGenerator\";\t\n"; - showEnvVarsInLog = 0; + shellScript = "rm \"$TARGET_BUILD_DIR/projectGenerator\"\ncp \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME\" \"$TARGET_BUILD_DIR/projectGenerator\";\t\n"; }; E42962A92163ECCD00A6A9E2 /* Run Script - Compile OF */ = { isa = PBXShellScriptBuildPhase; @@ -320,7 +319,6 @@ baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = NONE; diff --git a/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme b/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme index 3ae4128d..0857ea69 100644 --- a/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme +++ b/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme @@ -23,12 +23,10 @@ - - + shouldUseLaunchSchemeArgsEnv = "YES"> + + - + - - + + + + + + - + + + + + + + splitStringOnceByLeft(const string &source, const string &delimiter) { size_t pos = source.find(delimiter); vector res; if(pos == string::npos) { - res.push_back(source); + res.emplace_back(source); return res; } - res.push_back(source.substr(0, pos)); - res.push_back(source.substr(pos + delimiter.length())); + res.emplace_back(source.substr(0, pos)); + res.emplace_back(source.substr(pos + delimiter.length())); return res; } @@ -88,7 +92,6 @@ string ofAddon::stateName(ofAddon::ConfigParseState state){ default: return "unknown"; } - } bool ofAddon::checkCorrectPlatform(ConfigParseState state){ @@ -185,8 +188,8 @@ void ofAddon::addReplaceStringVector(std::vector & variable, std::s } } - if(prefix=="" || values[i].find(pathToOF.string())==0 || ofFilePath::isAbsolute(values[i])) variable.push_back(values[i]); - else variable.push_back(ofFilePath::join(prefix,values[i])); + if(prefix=="" || values[i].find(pathToOF.string())==0 || ofFilePath::isAbsolute(values[i])) variable.emplace_back(values[i]); + else variable.emplace_back(ofFilePath::join(prefix,values[i])); } } } @@ -203,12 +206,10 @@ void ofAddon::addReplaceStringVector(vector & variable, string va if (!addToVariable) variable.clear(); std::regex findVar("(\\$\\()(.+)(\\))"); - // FIXME: iterator - for (int i = 0; i<(int)values.size(); i++) { - if (values[i] != "") { - + for (auto & v : values) { + if (v != "") { std::smatch varMatch; - if(std::regex_search(values[i], varMatch, findVar)) { + if(std::regex_search(v, varMatch, findVar)) { if (varMatch.size() > 2) { string varName = varMatch[2].str(); string varValue; @@ -217,15 +218,15 @@ void ofAddon::addReplaceStringVector(vector & variable, string va }else if(getenv(varName.c_str())){ varValue = getenv(varName.c_str()); } - ofStringReplace(values[i],"$("+varName+")",varValue); - ofLogVerbose("ofAddon") << "addon config: substituting " << varName << " with " << varValue << " = " << values[i] << std::endl; + ofStringReplace(v,"$("+varName+")",varValue); + ofLogVerbose("ofAddon") << "addon config: substituting " << varName << " with " << varValue << " = " << v << std::endl; } } - if (prefix == "" || values[i].find(pathToOF.string()) == 0 || ofFilePath::isAbsolute(values[i])) { - variable.push_back({ values[i], "", "" }); + if (prefix == "" || v.find(pathToOF.string()) == 0 || ofFilePath::isAbsolute(v)) { + variable.push_back({ v, "", "" }); } else { - variable.push_back({ ofFilePath::join(prefix, values[i]), "", "" }); + variable.push_back( { ofFilePath::join(prefix, v), "", "" } ); } } } @@ -476,15 +477,14 @@ void ofAddon::parseConfig(){ } } -using std::cout; -using std::endl; bool ofAddon::fromFS(fs::path path, const std::string & platform){ +// cout << "ofAddon::fromFS path : " << path << endl; + clear(); this->platform = platform; fs::path prefixPath; - // FIXME: remove this fs::path containedPath { "" }; if(isLocalAddon){ @@ -502,21 +502,21 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ return false; } - auto srcPath = path / "src"; + fs::path srcPath = path / "src"; if (fs::exists(srcPath)) { getFilesRecursively(srcPath, srcFiles); } - // FIXME: srcFiles to fs::path + // MARK: srcFiles to fs::path + // not possible today because there are string based exclusion functions for (auto & s : srcFiles) { fs::path folder; auto srcFS = fs::path(prefixPath / fs::relative(s, containedPath)); if (isLocalAddon) { folder = srcFS.parent_path(); } else { - folder = fs::path(s).parent_path(); - folder = fs::relative(folder, containedPath); + folder = fs::relative(fs::path(s).parent_path(), containedPath); } s = srcFS.string(); filesToFolders[s] = folder.string(); @@ -527,138 +527,79 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ getPropsRecursively(addonPath.string(), propsFiles, platform); } - // FIXME: propsFiles to fs::path - + int i = 0; for (auto & s : propsFiles) { - - fs::path folder; + auto srcFS = fs::path(prefixPath / fs::relative(s, containedPath)); if (isLocalAddon) { - // FIXME: test if local addons is working ok - folder = fs::path("local_addons") / fs::path(s).parent_path(); +// folder = fs::path("local_addons") / fs::path(s).parent_path(); + folder = srcFS.parent_path(); } else { - folder = fs::path(s).parent_path(); - folder = fs::relative(folder, containedPath); + folder = fs::relative(fs::path(s).parent_path(), containedPath); } - - s = fs::path(prefixPath / fs::relative(s, containedPath)).string(); + s = srcFS.string(); + propsFiles[i] = folder.string(); + i++; } -// for (int i = 0; i < (int)propsFiles.size(); i++) { -// propsFiles[i].erase(propsFiles[i].begin(), propsFiles[i].begin() + containedPath.string().length()); -// int end = propsFiles[i].rfind(fs::path("/").make_preferred().string()); -// int init = 0; -// string folder; -// if (!isLocalAddon) { -// folder = propsFiles[i].substr(init, end); -// } -// else { -// init = propsFiles[i].find(name); -// folder = ofFilePath::join("local_addons", propsFiles[i].substr(init, end - init)); -// } -// propsFiles[i] = prefixPath.string() + propsFiles[i]; -// } - - fs::path libsPath = path / "libs"; vector < string > libFiles; if (fs::exists(libsPath)) { getLibsRecursively(libsPath, libFiles, libs, platform); - if (platform == "osx" || platform == "ios"){ getFrameworksRecursively(libsPath, frameworks, platform); } - if(platform == "vs" || platform == "msys2"){ getDllsRecursively(libsPath, dllsToCopy, platform); } } - else { -// cout << "NO " << fs::current_path() << endl; - } -// cout << "libFiles " << endl; for (auto & s : libFiles) { fs::path folder; auto srcFS = fs::path(prefixPath / fs::relative(s, containedPath)); - if (isLocalAddon) { - // FIXME: test if local addons is working ok - folder = fs::path("local_addons") / fs::path(s).parent_path(); folder = srcFS.parent_path(); } else { - folder = fs::path(s).parent_path(); - folder = fs::relative(folder, containedPath); + folder = fs::relative(fs::path(s).parent_path(), containedPath); } - s = srcFS.string(); srcFiles.emplace_back(s); filesToFolders[s] = folder.string(); } - + // changing libs folder from absolute to relative. for (auto & l : libs) { l.path = fs::path(prefixPath / fs::relative(l.path, containedPath)).string(); } - -// for (int i = 0; i < (int)libs.size(); i++){ -// -// cout << "libs path before " << libs[i].path << endl; -// // does libs[] have any path ? let's fix if so. -// int end = libs[i].path.rfind(fs::path("/").make_preferred().string()); -// if (end > 0){ -// libs[i].path.erase (libs[i].path.begin(), libs[i].path.begin() + containedPath.string().length()); -// libs[i].path = prefixPath.string() + libs[i].path; -// } -// cout << "libs path after " << libs[i].path << endl; -// -// } - - for (int i = 0; i < (int)frameworks.size(); i++){ - cout << frameworks[i] << endl; + for (auto & f : frameworks) { +// cout << f << endl; // knowing if we are system framework or not is important.... - bool bIsSystemFramework = false; - size_t foundUnixPath = frameworks[i].find('/'); - size_t foundWindowsPath = frameworks[i].find('\\'); + size_t foundUnixPath = f.find('/'); + size_t foundWindowsPath = f.find('\\'); if (foundUnixPath==std::string::npos && foundWindowsPath==std::string::npos){ bIsSystemFramework = true; // we have no "path" so we are system } if (bIsSystemFramework){ - ; // do we need to do anything here? - } else { + fs::path fPath { f }; + fs::path rel = fs::relative (f, pathToProject); + fs::path folderFS = rel.parent_path(); + filesToFolders[f] = folderFS.string(); - frameworks[i].erase (frameworks[i].begin(), frameworks[i].begin() + containedPath.string().length()); - - int init = 0; - int end = frameworks[i].rfind(fs::path("/").make_preferred().string()); - - string folder; - if (!isLocalAddon) { - folder = frameworks[i].substr(init, end); - } - else { - init = frameworks[i].find(name); - folder = ofFilePath::join("local_addons", frameworks[i].substr(init, end - init)); - } - - frameworks[i] = prefixPath.string() + frameworks[i]; - - cout << frameworks[i] << endl; - cout << folder << endl; - cout << "----" << endl; - filesToFolders[frameworks[i]] = folder; - + // cout << "pathToProject = " << pathToProject << endl; +// cout << "rel " << rel << endl; +// cout << "folderFS " << folderFS << endl; +// cout << "----" << endl; } } @@ -667,22 +608,22 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ // get every folder in addon/src and addon/libs vector < string > libFolders; - if(ofDirectory(libsPath).exists()){ + if (fs::exists(libsPath)) { getFoldersRecursively(libsPath, libFolders, platform); } vector < string > srcFolders; - if(ofDirectory(srcPath).exists()){ - getFoldersRecursively(path / "src", srcFolders, platform); + if (fs::exists(srcPath)) { + getFoldersRecursively(srcPath, srcFolders, platform); } // convert paths to relative for (auto & l : libFolders) { - paths.push_back({ prefixPath / fs::relative(fs::path(l), containedPath) }); + paths.emplace_back( prefixPath / fs::relative(fs::path(l), containedPath) ); } for (auto & l : srcFolders) { - paths.push_back({ prefixPath / fs::relative(fs::path(l), containedPath) }); + paths.emplace_back( prefixPath / fs::relative(fs::path(l), containedPath) ); } paths.sort(); //paths.unique(); // unique not needed anymore. everything is carefully inserted now. diff --git a/commandLine/src/addons/ofAddon.h b/commandLine/src/addons/ofAddon.h index e94fcf23..a9f12842 100644 --- a/commandLine/src/addons/ofAddon.h +++ b/commandLine/src/addons/ofAddon.h @@ -8,9 +8,9 @@ #ifndef OFADDON_H_ #define OFADDON_H_ -#include #include "ofConstants.h" #include "LibraryBinary.h" +#include // About Metadata @@ -98,7 +98,7 @@ class ofAddon { void clear(); // this is source files: - std::map < std::string, std::string > filesToFolders; //the addons has had, for each file, + std::unordered_map < std::string, std::string > filesToFolders; //the addons has had, for each file, //sometimes a listing of what folder to put it in, such as "addons/ofxOsc/src" std::vector < std::string > srcFiles; diff --git a/commandLine/src/main.cpp b/commandLine/src/main.cpp index cc6717ca..f9ccdd03 100644 --- a/commandLine/src/main.cpp +++ b/commandLine/src/main.cpp @@ -1,23 +1,28 @@ //#include "ofMain.h" + #include "optionparser.h" #include "defines.h" + +#include "Utils.h" // TODO: remove, temporary, only for alert function + +//cxxopts::Options options("Project Generator", "OpenFrameworks tool to generate projects"); enum optionIndex { UNKNOWN, HELP, PLUS, RECURSIVE, LISTTEMPLATES, PLATFORMS, ADDONS, OFPATH, VERBOSE, TEMPLATE, DRYRUN, SRCEXTERNAL, VERSION}; constexpr option::Descriptor usage[] = { - {UNKNOWN, 0, "", "",option::Arg::None, "Options:\n" }, - {HELP, 0,"h", "help",option::Arg::None, " --help \tPrint usage and exit." }, - {RECURSIVE, 0,"r","recursive",option::Arg::None, " --recursive, -r \tupdate recursively (applies only to update)" }, - {LISTTEMPLATES, 0,"l","listtemplates",option::Arg::None, " --listtemplates, -l \tlist templates available for the specified or current platform(s)" }, - {PLATFORMS, 0,"p","platforms",option::Arg::Optional, " --platforms, -p \tplatform list (such as osx, ios, winvs)" }, - {ADDONS, 0,"a","addons",option::Arg::Optional, " --addons, -a \taddon list (such as ofxOpenCv, ofxGui, ofxXmlSettings)" }, - {OFPATH, 0,"o","ofPath",option::Arg::Optional, " --ofPath, -o \tpath to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead" }, - {VERBOSE, 0,"v","verbose",option::Arg::None, " --verbose, -v \trun verbose" }, - {TEMPLATE, 0,"t","template",option::Arg::Optional, " --template, -t \tproject template" }, - {DRYRUN, 0,"d","dryrun",option::Arg::None, " --dryrun, -d \tdry run, don't change files" }, - {SRCEXTERNAL, 0,"s","source",option::Arg::Optional, " --source, -s \trelative or absolute path to source or include folders external to the project (such as ../../../../common_utils/" }, - {VERSION, 0, "w", "version", option::Arg::None, " --version, -w \treturn the current version"}, - {0,0,0,0,0,0} + {UNKNOWN, 0, "", "",option::Arg::None, "Options:\n" }, + {HELP, 0,"h", "help",option::Arg::None, " --help \tPrint usage and exit." }, + {RECURSIVE, 0,"r","recursive",option::Arg::None, " --recursive, -r \tupdate recursively (applies only to update)" }, + {LISTTEMPLATES, 0,"l","listtemplates",option::Arg::None, " --listtemplates, -l \tlist templates available for the specified or current platform(s)" }, + {PLATFORMS, 0,"p","platforms",option::Arg::Optional, " --platforms, -p \tplatform list (such as osx, ios, winvs)" }, + {ADDONS, 0,"a","addons",option::Arg::Optional, " --addons, -a \taddon list (such as ofxOpenCv, ofxGui, ofxXmlSettings)" }, + {OFPATH, 0,"o","ofPath",option::Arg::Optional, " --ofPath, -o \tpath to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead" }, + {VERBOSE, 0,"v","verbose",option::Arg::None, " --verbose, -v \trun verbose" }, + {TEMPLATE, 0,"t","template",option::Arg::Optional, " --template, -t \tproject template" }, + {DRYRUN, 0,"d","dryrun",option::Arg::None, " --dryrun, -d \tdry run, don't change files" }, + {SRCEXTERNAL, 0,"s","source",option::Arg::Optional, " --source, -s \trelative or absolute path to source or include folders external to the project (such as ../../../../common_utils/" }, + {VERSION, 0, "w", "version", option::Arg::None, " --version, -w \treturn the current version"}, + {0,0,0,0,0,0} }; #include "qtcreatorproject.h" @@ -26,6 +31,9 @@ constexpr option::Descriptor usage[] = #include "androidStudioProject.h" #include "Utils.h" +using std::cout; +using std::endl; + namespace fs = of::filesystem; #define EXIT_OK 0 @@ -36,9 +44,9 @@ namespace fs = of::filesystem; //----------------------------------------------------- enum pgMode { - PG_MODE_NONE, - PG_MODE_CREATE, - PG_MODE_UPDATE + PG_MODE_NONE, + PG_MODE_CREATE, + PG_MODE_UPDATE }; @@ -46,14 +54,15 @@ float startTime; int nProjectsUpdated; int nProjectsCreated; -std::string directoryForRecursion; -std::string projectPath; -std::string ofPath; + +//std::string projectPath; +fs::path projectPath; +fs::path ofPath; std::vector addons; +// FIXME: FS std::vector srcPaths; std::vector targets; std::string ofPathEnv; -std::string currentWorkingDirectory; std::string templateName; bool busingEnvVar; @@ -67,580 +76,525 @@ bool bListTemplates; // did we request help? bool bDryRun; // do dry run (useful for debugging recursive update) - - //------------------------------------------- void consoleSpace() { - std::cout << std::endl; + std::cout << std::endl; } -void printVersion() -{ - std::cout << OFPROJECTGENERATOR_MAJOR_VERSION << "." << OFPROJECTGENERATOR_MINOR_VERSION << "." << OFPROJECTGENERATOR_PATCH_VERSION << std::endl; +void printVersion() { + std::cout << OFPROJECTGENERATOR_MAJOR_VERSION << "." << OFPROJECTGENERATOR_MINOR_VERSION << "." << OFPROJECTGENERATOR_PATCH_VERSION << std::endl; } bool printTemplates() { - - std::cout << "getOFRoot() " << getOFRoot() << std::endl; - - if(targets.size()>1){ + if(targets.size()>1){ std::vector> allPlatformsTemplates; - for(auto & target: targets){ - auto templates = getTargetProject(target)->listAvailableTemplates(getTargetString(target)); - allPlatformsTemplates.push_back(templates); - } + for(auto & target: targets){ + auto templates = getTargetProject(target)->listAvailableTemplates(getTargetString(target)); + allPlatformsTemplates.emplace_back(templates); + } std::set commonTemplates; - for(auto & templates: allPlatformsTemplates){ - for(auto & t: templates){ - bool foundInAll = true; - for(auto & otherTemplates: allPlatformsTemplates){ - auto found = false; - for(auto & otherT: otherTemplates){ - if(otherT.name == t.name){ - found = true; - continue; - } - } - foundInAll &= found; - } - if(foundInAll){ - commonTemplates.insert(t); - } - } - } - if(commonTemplates.empty()){ - ofLogNotice() << "No templates available for all targets"; - return false; - }else{ - ofLogNotice() << "Templates available for all targets"; - for(auto & t: commonTemplates){ - ofLogNotice() << t.name << "\t\t" << t.description; - } - return true; - } - }else{ - bool templatesFound = false; - for(auto & target: targets){ - ofLogNotice() << "Templates for target " << getTargetString(target); - auto templates = getTargetProject(target)->listAvailableTemplates(getTargetString(target)); - for(auto & templateConfig: templates){ - ofLogNotice() << templateConfig.name << "\t\t" << templateConfig.description; - } - consoleSpace(); - templatesFound = !templates.empty(); - } - return templatesFound; - } -} - -void addPlatforms(std::string value) { - - targets.clear(); - std::vector < std::string > platforms = ofSplitString(value, ",", true, true); - - for (size_t i = 0; i < platforms.size(); i++) { - if (platforms[i] == "linux") { - targets.push_back(OF_TARGET_LINUX); - } - else if (platforms[i] == "linux64") { - targets.push_back(OF_TARGET_LINUX64); - } - else if (platforms[i] == "linuxarmv6l") { - targets.push_back(OF_TARGET_LINUXARMV6L); - } - else if (platforms[i] == "linuxarmv7l") { - targets.push_back(OF_TARGET_LINUXARMV7L); - } - else if (platforms[i] == "msys2") { - targets.push_back(OF_TARGET_MINGW); - } - else if (platforms[i] == "vs") { - targets.push_back(OF_TARGET_WINVS); - } - else if (platforms[i] == "osx") { - targets.push_back(OF_TARGET_OSX); - } - else if (platforms[i] == "ios") { - targets.push_back(OF_TARGET_IPHONE); - } - else if (platforms[i] == "android") { - targets.push_back(OF_TARGET_ANDROID); - } - else if (platforms[i] == "allplatforms") { - targets.push_back(OF_TARGET_LINUX); - targets.push_back(OF_TARGET_LINUX64); - targets.push_back(OF_TARGET_LINUXARMV6L); - targets.push_back(OF_TARGET_LINUXARMV7L); - targets.push_back(OF_TARGET_MINGW); - targets.push_back(OF_TARGET_WINVS); - targets.push_back(OF_TARGET_OSX); - targets.push_back(OF_TARGET_IOS); - targets.push_back(OF_TARGET_ANDROID); - }else{ - ofLogError() << "platform " << platforms[i] << " not valid"; - } - } + for(auto & templates: allPlatformsTemplates){ + for(auto & t: templates){ + bool foundInAll = true; + for(auto & otherTemplates: allPlatformsTemplates){ + auto found = false; + for(auto & otherT: otherTemplates){ + if(otherT.name == t.name){ + found = true; + continue; + } + } + foundInAll &= found; + } + if(foundInAll){ + commonTemplates.insert(t); + } + } + } + if(commonTemplates.empty()){ + ofLogNotice() << "No templates available for all targets"; + return false; + }else{ + ofLogNotice() << "Templates available for all targets"; + for(auto & t: commonTemplates){ + ofLogNotice() << t.name << "\t\t" << t.description; + } + return true; + } + }else{ + bool templatesFound = false; + for(auto & target: targets){ + ofLogNotice() << "Templates for target " << getTargetString(target); + auto templates = getTargetProject(target)->listAvailableTemplates(getTargetString(target)); + for(auto & templateConfig: templates){ + ofLogNotice() << templateConfig.name << "\t\t" << templateConfig.description; + } + consoleSpace(); + templatesFound = !templates.empty(); + } + return templatesFound; + } } +void addPlatforms(const std::string & value) { + targets.clear(); + std::vector < std::string > platforms = ofSplitString(value, ",", true, true); -bool isGoodProjectPath(std::string path) { - - ofDirectory dir(path); - if (!dir.isDirectory()) { - return false; - } - - dir.listDir(); - bool bHasSrc = false; - for (size_t i = 0; i < dir.size(); i++) { - if (dir.getName(i) == "src") { - bHasSrc = true; - } - } + for (auto & p : platforms) { + if (p == "linux") { + targets.emplace_back(OF_TARGET_LINUX); + } + else if (p == "linux64") { + targets.emplace_back(OF_TARGET_LINUX64); + } + else if (p == "linuxarmv6l") { + targets.emplace_back(OF_TARGET_LINUXARMV6L); + } + else if (p == "linuxarmv7l") { + targets.emplace_back(OF_TARGET_LINUXARMV7L); + } + else if (p == "msys2") { + targets.emplace_back(OF_TARGET_MINGW); + } + else if (p == "vs") { + targets.emplace_back(OF_TARGET_WINVS); + } + else if (p == "osx") { + targets.emplace_back(OF_TARGET_OSX); + } + else if (p == "ios") { + targets.emplace_back(OF_TARGET_IPHONE); + } + else if (p == "android") { + targets.emplace_back(OF_TARGET_ANDROID); + } + else if (p == "allplatforms") { + targets = { + OF_TARGET_LINUX, + OF_TARGET_LINUX64, + OF_TARGET_LINUXARMV6L, + OF_TARGET_LINUXARMV7L, + OF_TARGET_MINGW, + OF_TARGET_WINVS, + OF_TARGET_OSX, + OF_TARGET_IOS, + OF_TARGET_ANDROID, + }; + }else{ + ofLogError() << "platform " << p << " not valid"; + } + } +} - if (bHasSrc == true) { - return true; - } - else { - return false; - } +bool containsFolder(fs::path path, std::string folderName) { + bool contains = false; + for (const auto & entry : fs::directory_iterator(path)) { + auto f = entry.path(); + if (f.filename() == folderName) { + contains = true; + break; + } + } + return contains; } -bool isGoodOFPath(std::string path) { - - ofDirectory dir(path); - if (!dir.isDirectory()) { - ofLogError() << "ofPath seems wrong... not a directory"; - return false; - } - - dir.listDir(); - bool bHasTemplates = false; - for (size_t i = 0; i < dir.size(); i++) { - if (dir.getName(i) == "scripts") { - bHasTemplates = true; - } - } - - if (bHasTemplates == true) { - return true; - } - else { - ofLogError() << "ofPath seems wrong... no scripts / templates directory"; - return false; - } +bool isGoodProjectPath(fs::path path) { + if (!fs::is_directory(path)) return false; + return containsFolder(path, "src"); +} +bool isGoodOFPath(fs::path path) { + if (!fs::is_directory(path)) { + ofLogError() << "ofPath seems wrong... not a directory"; + return false; + } + bool bHasTemplates = containsFolder(path, "scripts"); + if (!bHasTemplates) ofLogError() << "ofPath seems wrong... no scripts / templates directory"; + return bHasTemplates; } +void updateProject(const fs::path & path, ofTargetPlatform target, bool bConsiderParameterAddons = true) { + // bConsiderParameterAddons = do we consider that the user could call update with a new set of addons + // either we read the addons.make file, or we look at the parameter list. + // if we are updating recursively, we *never* consider addons passed as parameters. -void updateProject(std::string path, ofTargetPlatform target, bool bConsiderParameterAddons = true) { - // bConsiderParameterAddons = do we consider that the user could call update with a new set of addons - // either we read the addons.make file, or we look at the parameter list. - // if we are updating recursively, we *never* consider addons passed as parameters. + ofLogNotice() << "updating project " << path; + auto project = getTargetProject(target); + if (!bDryRun) project->create(path, templateName); - ofLogNotice() << "updating project " << path; - auto project = getTargetProject(target); + if(bConsiderParameterAddons && bAddonsPassedIn){ - if (!bDryRun) project->create(path, templateName); + //cout << "---->> addons.size " << addons.size() << endl; + for (auto & a : addons) { + cout << a << endl; + } + + for(auto & addon: addons){ + project->addAddon(addon); + } + }else{ + ofLogNotice() << "parsing addons.make"; + project->parseAddons(); + } - if(bConsiderParameterAddons && bAddonsPassedIn){ - for(auto & addon: addons){ - project->addAddon(addon); - } - }else{ - ofLogNotice() << "parsing addons.make"; - project->parseAddons(); - } - - if(!bDryRun){ - for(auto & srcPath : srcPaths){ - project->addSrcRecursively(srcPath); - } - } + if(!bDryRun){ + for(auto & srcPath : srcPaths){ + project->addSrcRecursively(srcPath); + } + } - if (!bDryRun) project->save(); + if (!bDryRun) project->save(); } +void recursiveUpdate(const fs::path & path, ofTargetPlatform target) { + // first, bail if it's just a file + if (!fs::is_directory(path)) return; -void recursiveUpdate(std::string path, ofTargetPlatform target) { - - ofDirectory dir(path); - - - // first, bail if it's just a file - if (dir.isDirectory() == false) return; - - // second check if this is a folder that has src in it - if (isGoodProjectPath(path)) { - nProjectsUpdated++; - auto project = getTargetProject(target); - updateProject(path, target, false); - return; - } - - // finally, recursively look at this - dir.listDir(); - for (size_t i = 0; i < dir.size(); i++) { - ofDirectory subDir(dir.getPath(i)); - if (subDir.isDirectory()) { - recursiveUpdate(dir.getPath(i), target); - } - } + // second check if this is a folder that has src in it + if (isGoodProjectPath(path)) { + nProjectsUpdated++; + auto project = getTargetProject(target); + updateProject(path, target, false); + return; + } + + // finally, recursively look at this + for (const auto & entry : fs::directory_iterator(path)) { + auto f = entry.path(); + if (fs::is_directory(f)) { + recursiveUpdate(f, target); + } + } } void printHelp(){ - - consoleSpace(); - - std::string header = ""; - header += "\tprojectGenerator [options] pathName\n\n"; - header += "if pathName exists, project is updated\n"; - header += "if pathName doesn't exist, project is created\n"; - header += "(pathName must follow options, which can come in any order)"; - std::cout << header << std::endl; - - consoleSpace(); - - option::printUsage(std::cout, usage); - - consoleSpace(); - - std::string footer = ""; - footer += "examples:\n\n"; - footer += - STRINGIFY( - projectGenerator -o"../../../../" ../../../../apps/myApps/newExample - ); - footer += "\n"; - footer += "(create a project called newExample using a relative path for the OF root and the project. note the relative path may be different depending on where this app is located)"; - footer += "\n\n"; - footer += - STRINGIFY( - projectGenerator -r -o"../../../../" ../../../../examples - ); - footer += "\n"; - footer += "(recursively update the examples folder)"; - footer += "\n\n"; - footer += - STRINGIFY( - projectGenerator -o"../../../../" -a"ofxXmlSettings, ofxOpenCv" ../../../../apps/myApps/newExample - ); - footer += "\n"; - footer += "(create / update an example with addons)"; - std::cout << footer << std::endl; - - consoleSpace(); + consoleSpace(); + + std::string header = ""; + header += "\tprojectGenerator [options] pathName\n\n"; + header += "if pathName exists, project is updated\n"; + header += "if pathName doesn't exist, project is created\n"; + header += "(pathName must follow options, which can come in any order)"; + std::cout << header << std::endl; + + consoleSpace(); + + option::printUsage(std::cout, usage); + + consoleSpace(); + + std::string footer = ""; + footer += "examples:\n\n"; + footer += + STRINGIFY( + projectGenerator -o"../../../../" ../../../../apps/myApps/newExample + ); + footer += "\n"; + footer += "(create a project called newExample using a relative path for the OF root and the project. note the relative path may be different depending on where this app is located)"; + footer += "\n\n"; + footer += + STRINGIFY( + projectGenerator -r -o"../../../../" ../../../../examples + ); + footer += "\n"; + footer += "(recursively update the examples folder)"; + footer += "\n\n"; + footer += + STRINGIFY( + projectGenerator -o"../../../../" -a"ofxXmlSettings, ofxOpenCv" ../../../../apps/myApps/newExample + ); + footer += "\n"; + footer += "(create / update an example with addons)"; + std::cout << footer << std::endl; + + consoleSpace(); } //------------------------------------------- -int main(int argc, char* argv[]){ - - - //------------------------------------------- pre-parse - bAddonsPassedIn = false; - bDryRun = false; - busingEnvVar = false; - bVerbose = false; - mode = PG_MODE_NONE; - bForce = false; - bRecursive = false; - bHelpRequested = false; - bListTemplates = false; - targets.push_back(ofGetTargetPlatform()); - startTime = 0; - nProjectsUpdated = 0; - nProjectsCreated = 0; - std::string projectName = ""; - projectPath = ""; - templateName = ""; - - - // ------------------------------------------------------ parse args - argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present - option::Stats stats(usage, argc, argv); - std::vector options(stats.options_max); - std::vector buffer(stats.buffer_max); - option::Parser parse(usage, argc, argv, &options[0], &buffer[0]); +int main(int argc, char** argv){ + + //------------------------------------------- pre-parse + bAddonsPassedIn = false; + bDryRun = false; + busingEnvVar = false; + bVerbose = false; + mode = PG_MODE_NONE; + bForce = false; + bRecursive = false; + bHelpRequested = false; + bListTemplates = false; + targets.emplace_back(ofGetTargetPlatform()); + startTime = 0; + nProjectsUpdated = 0; + nProjectsCreated = 0; + std::string projectName = ""; +// projectPath = ""; +// ofPath = ""; + templateName = ""; + + + // ------------------------------------------------------ parse args + argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present + option::Stats stats(usage, argc, argv); + std::vector options(stats.options_max); + std::vector buffer(stats.buffer_max); + + option::Parser parse(usage, argc, argv, &options[0], &buffer[0]); + if (parse.error()) { - return 1; } - if (options[HELP] || argc == 0) { + + if (options[HELP] || argc == 0) { ofLogError() << "No arguments"; - printHelp(); - - return EXIT_OK; - } - - // templates: - if (options[LISTTEMPLATES].count() > 0){ - bListTemplates = true; - } - - if (options[RECURSIVE].count() > 0){ - bRecursive = true; - } - - if (options[DRYRUN].count() > 0){ - bDryRun = true; - } - if (options[VERSION].count() > 0){ - printVersion(); - return EXIT_OK; - } - - if (options[VERBOSE].count() > 0){ - bVerbose = true; - } - - if (options[PLATFORMS].count() > 0){ - if (options[PLATFORMS].arg != NULL){ - std::string platformString(options[PLATFORMS].arg); - addPlatforms(platformString); - } - } - - if (options[ADDONS].count() > 0){ - bAddonsPassedIn = true; // could be empty - if (options[ADDONS].arg != NULL){ - std::string addonsString(options[ADDONS].arg); - addons = ofSplitString(addonsString, ",", true, true); - } - } - - if (options[SRCEXTERNAL].count() > 0){ - if (options[SRCEXTERNAL].arg != NULL){ - std::string srcString(options[SRCEXTERNAL].arg); - srcPaths = ofSplitString(srcString, ",", true, true); - } - } - - if (options[OFPATH].count() > 0){ - if (options[OFPATH].arg != NULL){ - std::string ofPathString(options[OFPATH].arg); - ofPath = ofPathString; - } - } - - if (options[TEMPLATE].count() > 0){ - if (options[TEMPLATE].arg != NULL){ - std::string templateString(options[TEMPLATE].arg); - templateName = templateString; - } - } - - - if (parse.nonOptionsCount() > 0){ - projectName = parse.nonOption(0); - } - - - // ------------------------------------------------------ post parse - - nProjectsUpdated = 0; - nProjectsCreated = 0; - of::priv::initutils(); - startTime = ofGetElapsedTimef(); - consoleSpace(); - - // try to get the OF_PATH as an environt variable - char* pPath; - pPath = getenv("PG_OF_PATH"); - if (pPath != NULL) { - ofPathEnv = std::string(pPath); - } - - if (ofPath == "" && ofPathEnv != "") { - busingEnvVar = true; - ofPath = ofPathEnv; - } - - currentWorkingDirectory = fs::current_path().string(); - - if (ofPath == "") { - - consoleSpace(); - ofLogError() << "no OF path set... please use -o or --ofPath or set a PG_OF_PATH environment variable"; - consoleSpace(); - - //------------------------------------ fix me - //printHelp(); - - //return - - return EXIT_USAGE; - } else { + printHelp(); + return EXIT_OK; + } + + // templates: + if (options[LISTTEMPLATES].count() > 0){ + bListTemplates = true; + } + + if (options[RECURSIVE].count() > 0){ + bRecursive = true; + } + + if (options[DRYRUN].count() > 0){ + bDryRun = true; + } + if (options[VERSION].count() > 0){ + printVersion(); + return EXIT_OK; + } + + if (options[VERBOSE].count() > 0){ + bVerbose = true; + } + + if (options[PLATFORMS].count() > 0){ + if (options[PLATFORMS].arg != NULL){ + std::string platformString(options[PLATFORMS].arg); + addPlatforms(platformString); + } + } + + if (options[ADDONS].count() > 0){ + bAddonsPassedIn = true; // could be empty + if (options[ADDONS].arg != NULL){ + std::string addonsString(options[ADDONS].arg); + addons = ofSplitString(addonsString, ",", true, true); + } + } + + if (options[SRCEXTERNAL].count() > 0){ + if (options[SRCEXTERNAL].arg != NULL){ + std::string srcString(options[SRCEXTERNAL].arg); + srcPaths = ofSplitString(srcString, ",", true, true); + } + } + + if (options[OFPATH].count() > 0){ + if (options[OFPATH].arg != NULL){ + ofPath = options[OFPATH].arg; + } + } + + if (options[TEMPLATE].count() > 0){ + if (options[TEMPLATE].arg != NULL){ + std::string templateString(options[TEMPLATE].arg); + templateName = templateString; + } + } + + if (parse.nonOptionsCount() > 0){ + projectName = parse.nonOption(0); + } + + + // ------------------------------------------------------ post parse + + nProjectsUpdated = 0; + nProjectsCreated = 0; + of::priv::initutils(); + startTime = ofGetElapsedTimef(); + consoleSpace(); + + // try to get the OF_PATH as an environt variable + char* pPath; + pPath = getenv("PG_OF_PATH"); + if (pPath != NULL) { + ofPathEnv = std::string(pPath); + } + + if (ofPath == "" && ofPathEnv != "") { + busingEnvVar = true; + ofPath = ofPathEnv; + } + + + if (ofPath.empty()) { + consoleSpace(); + ofLogError() << "no OF path set... please use -o or --ofPath or set a PG_OF_PATH environment variable"; + consoleSpace(); + + //------------------------------------ fix me + //printHelp(); + //return + + return EXIT_USAGE; + } else { // convert ofpath from relative to absolute by appending this to current path and calculating .. by canonical. - // FIXME: convert ofPath and functions to fs::path + // TODO: test generating projects from pg executing from other directories if (!fs::path(ofPath).is_absolute()) { - ofPath = fs::canonical(fs::current_path() / fs::path(ofPath)).string(); + ofPath = (fs::current_path() / fs::path(ofPath)).lexically_normal(); } - - - if (!isGoodOFPath(ofPath)) { - - return EXIT_USAGE; - } - - setOFRoot(ofPath); - - - } - - - if(bListTemplates){ - auto ret = printTemplates(); - consoleSpace(); - if(ret){ - - return EXIT_OK; - }else{ - - return EXIT_DATAERR; - } - } - - if (projectName != ""){ - if (ofFilePath::isAbsolute(projectName)) { - projectPath = projectName; - } else { - projectPath = fs::absolute(projectName).string(); - } - } else { - ofLogError() << "Missing project path"; - printHelp(); - consoleSpace(); - - return EXIT_USAGE; - } - - - if (ofDirectory(projectPath).exists()) { - mode = PG_MODE_UPDATE; - } - else { - mode = PG_MODE_CREATE; - } - - if (bVerbose){ - ofSetLogLevel(OF_LOG_VERBOSE); - } - - - if (mode == PG_MODE_CREATE) { - - - nProjectsCreated += 1; - - for (int i = 0; i < (int)targets.size(); i++) { - auto project = getTargetProject(targets[i]); - auto target = getTargetString(targets[i]); - - ofLogNotice() << "-----------------------------------------------"; - ofLogNotice() << "setting OF path to: " << ofPath; - if(busingEnvVar){ - ofLogNotice() << "from PG_OF_PATH environment variable"; - }else{ - ofLogNotice() << "from -o option"; - } - ofLogNotice() << "target platform is: " << target; - ofLogNotice() << "project path is: " << projectPath; - - if(templateName!=""){ - ofLogNotice() << "using additional template " << templateName; - } - - - ofLogNotice() << "setting up new project " << projectPath; - if (!bDryRun) project->create(projectPath, templateName); - - if (!bDryRun){ - for(auto & addon: addons){ - project->addAddon(addon); - } - for(auto & srcPath : srcPaths){ - project->addSrcRecursively(srcPath); - } - } - if (!bDryRun) project->save(); - - ofLogNotice() << "project created! "; - ofLogNotice() << "-----------------------------------------------"; - consoleSpace(); - - - } - } - else if (mode == PG_MODE_UPDATE) { - - if (!bRecursive) { - if (isGoodProjectPath(projectPath) || bForce) { - - - nProjectsUpdated += 1; - - - for (int i = 0; i < (int)targets.size(); i++) { - ofLogNotice() << "-----------------------------------------------"; - ofLogNotice() << "setting OF path to: " << ofPath; - if(busingEnvVar){ - ofLogNotice() << "from PG_OF_PATH environment variable"; - }else{ - ofLogNotice() << "from -o option"; - } - ofLogNotice() << "target platform is: " << getTargetString(targets[i]); - - if(templateName!=""){ - ofLogNotice() << "using additional template " << templateName; - } - updateProject(projectPath,targets[i]); - - ofLogNotice() << "project updated! "; - ofLogNotice() << "-----------------------------------------------"; - consoleSpace(); - - } - } - else { - ofLogError() << "there's no src folder in this project path to update, maybe use create instead? (or use force to force updating)"; - } - } - else { - for (int i = 0; i < (int)targets.size(); i++) { - ofLogNotice() << "-----------------------------------------------"; - ofLogNotice() << "updating an existing project"; - ofLogNotice() << "target platform is: " << getTargetString(targets[i]); - - recursiveUpdate(projectPath, targets[i]); - - ofLogNotice() << "project updated! "; - ofLogNotice() << "-----------------------------------------------"; - - } - } - - - } - - consoleSpace(); - float elapsedTime = ofGetElapsedTimef() - startTime; - if (nProjectsCreated > 0) std::cout << nProjectsCreated << " project created "; - if (nProjectsUpdated == 1) std::cout << nProjectsUpdated << " project updated "; - if (nProjectsUpdated > 1) std::cout << nProjectsUpdated << " projects updated "; - ofLogNotice() << "in " << elapsedTime << " seconds" << std::endl; - consoleSpace(); - - - return EXIT_OK; -} + if (!isGoodOFPath(ofPath)) { + return EXIT_USAGE; + } + setOFRoot(ofPath); + } + if(bListTemplates){ + auto ret = printTemplates(); + consoleSpace(); + if(ret){ + return EXIT_OK; + }else{ + return EXIT_DATAERR; + } + } + + if (projectName != ""){ + fs::path projectNameFS { projectName }; + if (projectNameFS.is_absolute()) { + projectPath = projectNameFS; + } else { + projectPath = fs::absolute(projectNameFS); + } + // so we remove the trailing dot when running PG last parameter as . + projectPath = projectPath.lexically_normal(); + } else { + ofLogError() << "Missing project path"; + printHelp(); + consoleSpace(); + + return EXIT_USAGE; + } + + if (fs::exists(projectPath) && fs::is_directory(projectPath)) { + mode = PG_MODE_UPDATE; + } + else { + mode = PG_MODE_CREATE; + } + //XAXA TEMP + mode = PG_MODE_CREATE; + + + if (bVerbose){ + ofSetLogLevel(OF_LOG_VERBOSE); + } + + if (mode == PG_MODE_CREATE) { + nProjectsCreated += 1; + + for (int i = 0; i < (int)targets.size(); i++) { + auto project = getTargetProject(targets[i]); + auto target = getTargetString(targets[i]); + + ofLogNotice() << "-----------------------------------------------"; + ofLogNotice() << "setting OF path to: " << ofPath; + if(busingEnvVar){ + ofLogNotice() << "from PG_OF_PATH environment variable"; + }else{ + ofLogNotice() << "from -o option"; + } + ofLogNotice() << "target platform is: " << target; + ofLogNotice() << "project path is: " << projectPath; + if(templateName!=""){ + ofLogNotice() << "using additional template " << templateName; + } + ofLogNotice() << "setting up new project " << projectPath; + if (!bDryRun) project->create(projectPath, templateName); + + + if (!bDryRun){ + for(auto & addon: addons){ + project->addAddon(addon); + } + for(auto & srcPath : srcPaths){ + project->addSrcRecursively(srcPath); + } + } + if (!bDryRun) project->save(); + + ofLogNotice() << "project created! "; + ofLogNotice() << "-----------------------------------------------"; + consoleSpace(); + } + } + + else if (mode == PG_MODE_UPDATE) { + if (!bRecursive) { + if (isGoodProjectPath(projectPath) || bForce) { + nProjectsUpdated += 1; + + for (int i = 0; i < (int)targets.size(); i++) { + ofLogNotice() << "-----------------------------------------------"; + ofLogNotice() << "setting OF path to: " << ofPath; + if(busingEnvVar){ + ofLogNotice() << "from PG_OF_PATH environment variable"; + }else{ + ofLogNotice() << "from -o option"; + } + ofLogNotice() << "target platform is: " << getTargetString(targets[i]); + + if(templateName!=""){ + ofLogNotice() << "using additional template " << templateName; + } + updateProject(projectPath,targets[i]); + + ofLogNotice() << "project updated! "; + ofLogNotice() << "-----------------------------------------------"; + consoleSpace(); + } + } + else { + ofLogError() << "there's no src folder in this project path to update, maybe use create instead? (or use force to force updating)"; + } + } + else { + for (int i = 0; i < (int)targets.size(); i++) { + ofLogNotice() << "-----------------------------------------------"; + ofLogNotice() << "updating an existing project"; + ofLogNotice() << "target platform is: " << getTargetString(targets[i]); + + recursiveUpdate(projectPath, targets[i]); + + ofLogNotice() << "project updated! "; + ofLogNotice() << "-----------------------------------------------"; + } + } + } + + consoleSpace(); + float elapsedTime = ofGetElapsedTimef() - startTime; + if (nProjectsCreated > 0) std::cout << nProjectsCreated << " project created "; + if (nProjectsUpdated == 1) std::cout << nProjectsUpdated << " project updated "; + if (nProjectsUpdated > 1) std::cout << nProjectsUpdated << " projects updated "; + ofLogNotice() << "in " << elapsedTime << " seconds" << std::endl; + consoleSpace(); + + return EXIT_OK; +} diff --git a/commandLine/src/optionparser.h b/commandLine/src/optionparser.h index 5ad67a4e..7cabb2f8 100644 --- a/commandLine/src/optionparser.h +++ b/commandLine/src/optionparser.h @@ -1,7 +1,7 @@ /* * The Lean Mean C++ Option Parser * - * Copyright (C) 2012 Matthias S. Benkmann + * Copyright (C) 2012-2017 Matthias S. Benkmann * * The "Software" in the following 2 paragraphs refers to this file containing * the code to The Lean Mean C++ Option Parser. @@ -43,11 +43,13 @@ * @brief This is the only file required to use The Lean Mean C++ Option Parser. * Just \#include it and you're set. * - * The Lean Mean C++ Option Parser handles the program's command line arguments + * The Lean Mean C++ Option Parser handles the program's command line arguments * (argc, argv). - * It supports the short and long option formats of getopt(), getopt_long() + * It supports the short and long option formats of getopt(), getopt_long() * and getopt_long_only() but has a more convenient interface. - * The following features set it apart from other option parsers: + * + * @par Feedback: + * Send questions, bug reports, feature requests etc. to: optionparser-feedback(a)lists.sourceforge.net * * @par Highlights: *
    @@ -82,18 +84,23 @@ * @endcode *
* @n - * Despite these features the code size remains tiny. + * Despite these features the code size remains tiny. * It is smaller than uClibc's GNU getopt() and just a * couple 100 bytes larger than uClibc's SUSv3 getopt(). @n * (This does not include the usage formatter, of course. But you don't have to use that.) * * @par Download: * Tarball with examples and test programs: - * optionparser-1.3.tar.gz @n + * optionparser-1.7.tar.gz @n * Just the header (this is all you really need): * optionparser.h * * @par Changelog: + * Version 1.7: Work on const-correctness. @n + * Version 1.6: Fix for MSC compiler. @n + * Version 1.5: Fixed 2 warnings about potentially uninitialized variables. @n + * Added const version of Option::next(). @n + * Version 1.4: Fixed 2 printUsage() bugs that messed up output with small COLUMNS values. @n * Version 1.3: Compatible with Microsoft Visual C++. @n * Version 1.2: Added @ref option::Option::namelen "Option::namelen" and removed the extraction * of short option characters into a special buffer. @n @@ -104,10 +111,6 @@ * options and non-options can be mixed. See * @ref option::Parser::parse() "Parser::parse()". * - * @par Feedback: - * Send questions, bug reports, feature requests etc. to: optionparser-feedback (a) lists.sourceforge.net - * @htmlonly @endhtmlonly - * * * @par Example program: * (Note: @c option::* identifiers are links that take you to their documentation.) @@ -215,20 +218,23 @@ #ifndef OPTIONPARSER_H_ #define OPTIONPARSER_H_ +#ifdef _MSC_VER +#include +#pragma intrinsic(_BitScanReverse) +#endif + /** @brief The namespace of The Lean Mean C++ Option Parser. */ namespace option { #ifdef _MSC_VER -#include -#pragma intrinsic(_BitScanReverse) struct MSC_Builtin_CLZ { static int builtin_clz(unsigned x) { - unsigned long index; - _BitScanReverse(&index, x); - return 32-index; // int is always 32bit on Windows, even for target x64 + unsigned long index; + _BitScanReverse(&index, x); + return 32-index; // int is always 32bit on Windows, even for target x64 } }; #define __builtin_clz(x) MSC_Builtin_CLZ::builtin_clz(x) @@ -526,7 +532,7 @@ class Option */ int type() const { - return desc == 0 ? 0 : desc->type; + return desc == 0 ? 0 : desc->type; } /** @@ -535,7 +541,7 @@ class Option */ int index() const { - return desc == 0 ? -1 : (int)desc->index; + return desc == 0 ? -1 : (int)desc->index; } /** @@ -550,16 +556,16 @@ class Option * * Returns 0 when called for an unused/invalid option. */ - int count() - { - int c = (desc == 0 ? 0 : 1); - Option* p = first(); - while (!p->isLast()) - { - ++c; - p = p->next_; - }; - return c; + int count() const + { + int c = (desc == 0 ? 0 : 1); + const Option* p = first(); + while (!p->isLast()) + { + ++c; + p = p->next_; + }; + return c; } /** @@ -572,7 +578,7 @@ class Option */ bool isFirst() const { - return isTagged(prev_); + return isTagged(prev_); } /** @@ -585,7 +591,7 @@ class Option */ bool isLast() const { - return isTagged(next_); + return isTagged(next_); } /** @@ -601,10 +607,18 @@ class Option */ Option* first() { - Option* p = this; - while (!p->isFirst()) - p = p->prev_; - return p; + Option* p = this; + while (!p->isFirst()) + p = p->prev_; + return p; + } + + /** + * const version of Option::first(). + */ + const Option* first() const + { + return const_cast(this)->first(); } /** @@ -625,7 +639,15 @@ class Option */ Option* last() { - return first()->prevwrap(); + return first()->prevwrap(); + } + + /** + * const version of Option::last(). + */ + const Option* last() const + { + return first()->prevwrap(); } /** @@ -638,7 +660,7 @@ class Option */ Option* prev() { - return isFirst() ? 0 : prev_; + return isFirst() ? 0 : prev_; } /** @@ -651,7 +673,15 @@ class Option */ Option* prevwrap() { - return untag(prev_); + return untag(prev_); + } + + /** + * const version of Option::prevwrap(). + */ + const Option* prevwrap() const + { + return untag(prev_); } /** @@ -664,7 +694,15 @@ class Option */ Option* next() { - return isLast() ? 0 : next_; + return isLast() ? 0 : next_; + } + + /** + * const version of Option::next(). + */ + const Option* next() const + { + return isLast() ? 0 : next_; } /** @@ -677,7 +715,7 @@ class Option */ Option* nextwrap() { - return untag(next_); + return untag(next_); } /** @@ -692,12 +730,12 @@ class Option */ void append(Option* new_last) { - Option* p = last(); - Option* f = first(); - p->next_ = new_last; - new_last->prev_ = p; - new_last->next_ = tag(f); - f->prev_ = tag(new_last); + Option* p = last(); + Option* f = first(); + p->next_ = new_last; + new_last->prev_ = p; + new_last->next_ = tag(f); + f->prev_ = tag(new_last); } /** @@ -718,7 +756,7 @@ class Option */ operator const Option*() const { - return desc ? this : 0; + return desc ? this : 0; } /** @@ -739,7 +777,7 @@ class Option */ operator Option*() { - return desc ? this : 0; + return desc ? this : 0; } /** @@ -747,10 +785,10 @@ class Option * @ref desc, @ref name, @ref arg and @ref namelen. */ Option() : - desc(0), name(0), arg(0), namelen(0) + desc(0), name(0), arg(0), namelen(0) { - prev_ = tag(this); - next_ = tag(this); + prev_ = tag(this); + next_ = tag(this); } /** @@ -763,7 +801,7 @@ class Option */ Option(const Descriptor* desc_, const char* name_, const char* arg_) { - init(desc_, name_, arg_); + init(desc_, name_, arg_); } /** @@ -773,7 +811,7 @@ class Option */ void operator=(const Option& orig) { - init(orig.desc, orig.name, orig.arg); + init(orig.desc, orig.name, orig.arg); } /** @@ -783,7 +821,7 @@ class Option */ Option(const Option& orig) { - init(orig.desc, orig.name, orig.arg); + init(orig.desc, orig.name, orig.arg); } private: @@ -797,34 +835,34 @@ class Option */ void init(const Descriptor* desc_, const char* name_, const char* arg_) { - desc = desc_; - name = name_; - arg = arg_; - prev_ = tag(this); - next_ = tag(this); - namelen = 0; - if (name == 0) - return; - namelen = 1; - if (name[0] != '-') - return; - while (name[namelen] != 0 && name[namelen] != '=') - ++namelen; + desc = desc_; + name = name_; + arg = arg_; + prev_ = tag(this); + next_ = tag(this); + namelen = 0; + if (name == 0) + return; + namelen = 1; + if (name[0] != '-') + return; + while (name[namelen] != 0 && name[namelen] != '=') + ++namelen; } static Option* tag(Option* ptr) { - return (Option*) ((unsigned long long) ptr | 1); + return (Option*) ((unsigned long long) ptr | 1); } static Option* untag(Option* ptr) { - return (Option*) ((unsigned long long) ptr & ~1ull); + return (Option*) ((unsigned long long) ptr & ~1ull); } static bool isTagged(Option* ptr) { - return ((unsigned long long) ptr & 1); + return ((unsigned long long) ptr & 1); } }; @@ -887,16 +925,16 @@ struct Arg //! @brief For options that don't take an argument: Returns ARG_NONE. static ArgStatus None(const Option&, bool) { - return ARG_NONE; + return ARG_NONE; } //! @brief Returns ARG_OK if the argument is attached and ARG_IGNORE otherwise. static ArgStatus Optional(const Option& option, bool) { - if (option.arg && option.name[option.namelen] != 0) - return ARG_OK; - else - return ARG_IGNORE; + if (option.arg && option.name[option.namelen] != 0) + return ARG_OK; + else + return ARG_IGNORE; } }; @@ -939,7 +977,7 @@ struct Stats * @brief Creates a Stats object with counts set to 1 (for the sentinel element). */ Stats() : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { } @@ -953,34 +991,34 @@ struct Stats * See Parser::parse() for the meaning of the arguments. */ Stats(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt); + add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt); } //! @brief Stats(...) with non-const argv. Stats(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX Stats(...) (gnu==false). Stats(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX Stats(...) (gnu==false) with non-const argv. Stats(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } /** @@ -993,27 +1031,27 @@ struct Stats * See Parser::parse() for the meaning of the arguments. */ void add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false); + bool single_minus_longopt = false); //! @brief add() with non-const argv. void add(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) + bool single_minus_longopt = false) { - add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX add() (gnu==false). void add(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) + bool single_minus_longopt = false) { - add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX add() (gnu==false) with non-const argv. void add(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) + bool single_minus_longopt = false) { - add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } private: class CountOptionsAction; @@ -1051,7 +1089,7 @@ class Parser * @brief Creates a new Parser. */ Parser() : - op_count(0), nonop_count(0), nonop_args(0), err(false) + op_count(0), nonop_count(0), nonop_args(0), err(false) { } @@ -1060,34 +1098,34 @@ class Parser * @copydetails parse() */ Parser(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief Parser(...) with non-const argv. Parser(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX Parser(...) (gnu==false). Parser(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX Parser(...) (gnu==false) with non-const argv. Parser(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } /** @@ -1147,27 +1185,27 @@ class Parser * @c options[buffer[i].index()]. */ void parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1); + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1); //! @brief parse() with non-const argv. void parse(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) { - parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX parse() (gnu==false). void parse(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) { - parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX parse() (gnu==false) with non-const argv. void parse(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) + bool single_minus_longopt = false, int bufmax = -1) { - parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } /** @@ -1181,7 +1219,7 @@ class Parser */ int optionsCount() { - return op_count; + return op_count; } /** @@ -1200,7 +1238,7 @@ class Parser */ int nonOptionsCount() { - return nonop_count; + return nonop_count; } /** @@ -1216,7 +1254,7 @@ class Parser */ const char** nonOptions() { - return nonop_args; + return nonop_args; } /** @@ -1224,7 +1262,7 @@ class Parser */ const char* nonOption(int i) { - return nonOptions()[i]; + return nonOptions()[i]; } /** @@ -1244,7 +1282,7 @@ class Parser */ bool error() { - return err; + return err; } private: @@ -1258,7 +1296,7 @@ class Parser * @retval false iff an unrecoverable error occurred. */ static bool workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, - bool single_minus_longopt, bool print_errors, int min_abbr_len); + bool single_minus_longopt, bool print_errors, int min_abbr_len); /** * @internal @@ -1276,10 +1314,10 @@ class Parser */ static bool streq(const char* st1, const char* st2) { - while (*st1 != 0) - if (*st1++ != *st2++) - return false; - return (*st2 == 0 || *st2 == '='); + while (*st1 != 0) + if (*st1++ != *st2++) + return false; + return (*st2 == 0 || *st2 == '='); } /** @@ -1308,14 +1346,14 @@ class Parser */ static bool streqabbr(const char* st1, const char* st2, long long min) { - const char* st1start = st1; - while (*st1 != 0 && (*st1 == *st2)) - { - ++st1; - ++st2; - } + const char* st1start = st1; + while (*st1 != 0 && (*st1 == *st2)) + { + ++st1; + ++st2; + } - return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '='); + return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '='); } /** @@ -1326,9 +1364,9 @@ class Parser */ static bool instr(char ch, const char* st) { - while (*st != 0 && *st != ch) - ++st; - return *st == ch; + while (*st != 0 && *st != ch) + ++st; + return *st == ch; } /** @@ -1338,12 +1376,12 @@ class Parser */ static void shift(const char** args, int count) { - for (int i = 0; i > -count; --i) - { - const char* temp = args[i]; - args[i] = args[i - 1]; - args[i - 1] = temp; - } + for (int i = 0; i > -count; --i) + { + const char* temp = args[i]; + args[i] = args[i - 1]; + args[i - 1] = temp; + } } }; @@ -1364,7 +1402,7 @@ struct Parser::Action */ virtual bool perform(Option&) { - return true; + return true; } /** @@ -1377,9 +1415,9 @@ struct Parser::Action */ virtual bool finished(int numargs, const char** args) { - (void) numargs; - (void) args; - return true; + (void) numargs; + (void) args; + return true; } }; @@ -1397,16 +1435,16 @@ class Stats::CountOptionsAction: public Parser::Action * parsed Option. */ CountOptionsAction(unsigned* buffer_max_) : - buffer_max(buffer_max_) + buffer_max(buffer_max_) { } bool perform(Option&) { - if (*buffer_max == 0x7fffffff) - return false; // overflow protection: don't accept number of options that doesn't fit signed int - ++*buffer_max; - return true; + if (*buffer_max == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + ++*buffer_max; + return true; } }; @@ -1430,68 +1468,68 @@ class Parser::StoreOptionAction: public Parser::Action * @param bufmax_ number of slots in @c buffer_. @c -1 means "large enough". */ StoreOptionAction(Parser& parser_, Option options_[], Option buffer_[], int bufmax_) : - parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_) + parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_) { - // find first empty slot in buffer (if any) - int bufidx = 0; - while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx]) - ++bufidx; + // find first empty slot in buffer (if any) + int bufidx = 0; + while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx]) + ++bufidx; - // set parser's optionCount - parser.op_count = bufidx; + // set parser's optionCount + parser.op_count = bufidx; } bool perform(Option& option) { - if (bufmax < 0 || parser.op_count < bufmax) - { - if (parser.op_count == 0x7fffffff) - return false; // overflow protection: don't accept number of options that doesn't fit signed int - - buffer[parser.op_count] = option; - int idx = buffer[parser.op_count].desc->index; - if (options[idx]) - options[idx].append(buffer[parser.op_count]); - else - options[idx] = buffer[parser.op_count]; - ++parser.op_count; - } - return true; // NOTE: an option that is discarded because of a full buffer is not fatal + if (bufmax < 0 || parser.op_count < bufmax) + { + if (parser.op_count == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + + buffer[parser.op_count] = option; + int idx = buffer[parser.op_count].desc->index; + if (options[idx]) + options[idx].append(buffer[parser.op_count]); + else + options[idx] = buffer[parser.op_count]; + ++parser.op_count; + } + return true; // NOTE: an option that is discarded because of a full buffer is not fatal } bool finished(int numargs, const char** args) { - // only overwrite non-option argument list if there's at least 1 - // new non-option argument. Otherwise we keep the old list. This - // makes it easy to use default non-option arguments. - if (numargs > 0) - { - parser.nonop_count = numargs; - parser.nonop_args = args; - } + // only overwrite non-option argument list if there's at least 1 + // new non-option argument. Otherwise we keep the old list. This + // makes it easy to use default non-option arguments. + if (numargs > 0) + { + parser.nonop_count = numargs; + parser.nonop_args = args; + } - return true; + return true; } }; inline void Parser::parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], - Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax) + Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax) { StoreOptionAction action(*this, options, buffer, bufmax); err = !workhorse(gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len); } inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len, - bool single_minus_longopt) + bool single_minus_longopt) { // determine size of options array. This is the greatest index used in the usage + 1 int i = 0; while (usage[i].shortopt != 0) { - if (usage[i].index + 1 >= options_max) - options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel + if (usage[i].index + 1 >= options_max) + options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel - ++i; + ++i; } CountOptionsAction action(&buffer_max); @@ -1499,181 +1537,181 @@ inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char* } inline bool Parser::workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, - bool single_minus_longopt, bool print_errors, int min_abbr_len) + bool single_minus_longopt, bool print_errors, int min_abbr_len) { // protect against NULL pointer if (args == 0) - numargs = 0; + numargs = 0; int nonops = 0; while (numargs != 0 && *args != 0) { - const char* param = *args; // param can be --long-option, -srto or non-option argument - - // in POSIX mode the first non-option argument terminates the option list - // a lone minus character is a non-option argument - if (param[0] != '-' || param[1] == 0) - { - if (gnu) - { - ++nonops; - ++args; - if (numargs > 0) - --numargs; - continue; - } - else - break; - } - - // -- terminates the option list. The -- itself is skipped. - if (param[1] == '-' && param[2] == 0) - { - shift(args, nonops); - ++args; - if (numargs > 0) - --numargs; - break; - } - - bool handle_short_options; - const char* longopt_name; - if (param[1] == '-') // if --long-option - { - handle_short_options = false; - longopt_name = param + 2; - } - else - { - handle_short_options = true; - longopt_name = param + 1; //for testing a potential -long-option - } - - bool try_single_minus_longopt = single_minus_longopt; - bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid? - - do // loop over short options in group, for long options the body is executed only once - { - int idx; - - const char* optarg; - - /******************** long option **********************/ - if (handle_short_options == false || try_single_minus_longopt) - { - idx = 0; - while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name)) - ++idx; - - if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options - { - int i1 = 0; - while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len)) - ++i1; - if (usage[i1].longopt != 0) - { // now test if the match is unambiguous by checking for another match - int i2 = i1 + 1; - while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len)) - ++i2; - - if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx - idx = i1; - } - } - - // if we found something, disable handle_short_options (only relevant if single_minus_longopt) - if (usage[idx].longopt != 0) - handle_short_options = false; - - try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group - - optarg = longopt_name; - while (*optarg != 0 && *optarg != '=') - ++optarg; - if (*optarg == '=') // attached argument - ++optarg; - else - // possibly detached argument - optarg = (have_more_args ? args[1] : 0); - } - - /************************ short option ***********************************/ - if (handle_short_options) - { - if (*++param == 0) // point at the 1st/next option character - break; // end of short option group - - idx = 0; - while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt)) - ++idx; - - if (param[1] == 0) // if the potential argument is separate - optarg = (have_more_args ? args[1] : 0); - else - // if the potential argument is attached - optarg = param + 1; - } - - const Descriptor* descriptor = &usage[idx]; - - if (descriptor->shortopt == 0) /************** unknown option ********************/ - { - // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options - idx = 0; - while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0)) - ++idx; - descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]); - } - - if (descriptor != 0) - { - Option option(descriptor, param, optarg); - switch (descriptor->check_arg(option, print_errors)) - { - case ARG_ILLEGAL: - return false; // fatal - case ARG_OK: - // skip one element of the argument vector, if it's a separated argument - if (optarg != 0 && have_more_args && optarg == args[1]) - { - shift(args, nonops); - if (numargs > 0) - --numargs; - ++args; - } - - // No further short options are possible after an argument - handle_short_options = false; - - break; - case ARG_IGNORE: - case ARG_NONE: - option.arg = 0; - break; - } - - if (!action.perform(option)) - return false; - } - - } while (handle_short_options); - - shift(args, nonops); - ++args; - if (numargs > 0) - --numargs; + const char* param = *args; // param can be --long-option, -srto or non-option argument + + // in POSIX mode the first non-option argument terminates the option list + // a lone minus character is a non-option argument + if (param[0] != '-' || param[1] == 0) + { + if (gnu) + { + ++nonops; + ++args; + if (numargs > 0) + --numargs; + continue; + } + else + break; + } + + // -- terminates the option list. The -- itself is skipped. + if (param[1] == '-' && param[2] == 0) + { + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; + break; + } + + bool handle_short_options; + const char* longopt_name; + if (param[1] == '-') // if --long-option + { + handle_short_options = false; + longopt_name = param + 2; + } + else + { + handle_short_options = true; + longopt_name = param + 1; //for testing a potential -long-option + } + + bool try_single_minus_longopt = single_minus_longopt; + bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid? + + do // loop over short options in group, for long options the body is executed only once + { + int idx = 0; + + const char* optarg = 0; + + /******************** long option **********************/ + if (handle_short_options == false || try_single_minus_longopt) + { + idx = 0; + while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name)) + ++idx; + + if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options + { + int i1 = 0; + while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len)) + ++i1; + if (usage[i1].longopt != 0) + { // now test if the match is unambiguous by checking for another match + int i2 = i1 + 1; + while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len)) + ++i2; + + if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx + idx = i1; + } + } + + // if we found something, disable handle_short_options (only relevant if single_minus_longopt) + if (usage[idx].longopt != 0) + handle_short_options = false; + + try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group + + optarg = longopt_name; + while (*optarg != 0 && *optarg != '=') + ++optarg; + if (*optarg == '=') // attached argument + ++optarg; + else + // possibly detached argument + optarg = (have_more_args ? args[1] : 0); + } + + /************************ short option ***********************************/ + if (handle_short_options) + { + if (*++param == 0) // point at the 1st/next option character + break; // end of short option group + + idx = 0; + while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt)) + ++idx; + + if (param[1] == 0) // if the potential argument is separate + optarg = (have_more_args ? args[1] : 0); + else + // if the potential argument is attached + optarg = param + 1; + } + + const Descriptor* descriptor = &usage[idx]; + + if (descriptor->shortopt == 0) /************** unknown option ********************/ + { + // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options + idx = 0; + while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0)) + ++idx; + descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]); + } + + if (descriptor != 0) + { + Option option(descriptor, param, optarg); + switch (descriptor->check_arg(option, print_errors)) + { + case ARG_ILLEGAL: + return false; // fatal + case ARG_OK: + // skip one element of the argument vector, if it's a separated argument + if (optarg != 0 && have_more_args && optarg == args[1]) + { + shift(args, nonops); + if (numargs > 0) + --numargs; + ++args; + } + + // No further short options are possible after an argument + handle_short_options = false; + + break; + case ARG_IGNORE: + case ARG_NONE: + option.arg = 0; + break; + } + + if (!action.perform(option)) + return false; + } + + } while (handle_short_options); + + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; } // while if (numargs > 0 && *args == 0) // It's a bug in the caller if numargs is greater than the actual number - numargs = 0; // of arguments, but as a service to the user we fix this if we spot it. + numargs = 0; // of arguments, but as a service to the user we fix this if we spot it. if (numargs < 0) // if we don't know the number of remaining non-option arguments { // we need to count them - numargs = 0; - while (args[numargs] != 0) - ++numargs; + numargs = 0; + while (args[numargs] != 0) + ++numargs; } return action.finished(numargs + nonops, args - nonops); @@ -1691,12 +1729,12 @@ struct PrintUsageImplementation */ struct IStringWriter { - /** - * @brief Writes the given number of chars beginning at the given pointer somewhere. - */ - virtual void operator()(const char*, int) - { - } + /** + * @brief Writes the given number of chars beginning at the given pointer somewhere. + */ + virtual void operator()(const char*, int) + { + } }; /** @@ -1707,17 +1745,17 @@ struct PrintUsageImplementation template struct FunctionWriter: public IStringWriter { - Function* write; + Function* write; - virtual void operator()(const char* str, int size) - { - (*write)(str, size); - } + virtual void operator()(const char* str, int size) + { + (*write)(str, size); + } - FunctionWriter(Function* w) : - write(w) - { - } + FunctionWriter(Function* w) : + write(w) + { + } }; /** @@ -1728,17 +1766,17 @@ struct PrintUsageImplementation template struct OStreamWriter: public IStringWriter { - OStream& ostream; + OStream& ostream; - virtual void operator()(const char* str, int size) - { - ostream.write(str, size); - } + virtual void operator()(const char* str, int size) + { + ostream.write(str, size); + } - OStreamWriter(OStream& o) : - ostream(o) - { - } + OStreamWriter(OStream& o) : + ostream(o) + { + } }; /** @@ -1749,17 +1787,17 @@ struct PrintUsageImplementation template struct TemporaryWriter: public IStringWriter { - const Temporary& userstream; + const Temporary& userstream; - virtual void operator()(const char* str, int size) - { - userstream.write(str, size); - } + virtual void operator()(const char* str, int size) + { + userstream.write(str, size); + } - TemporaryWriter(const Temporary& u) : - userstream(u) - { - } + TemporaryWriter(const Temporary& u) : + userstream(u) + { + } }; /** @@ -1771,18 +1809,18 @@ struct PrintUsageImplementation template struct SyscallWriter: public IStringWriter { - Syscall* write; - int fd; + Syscall* write; + int fd; - virtual void operator()(const char* str, int size) - { - (*write)(fd, str, size); - } + virtual void operator()(const char* str, int size) + { + (*write)(fd, str, size); + } - SyscallWriter(Syscall* w, int f) : - write(w), fd(f) - { - } + SyscallWriter(Syscall* w, int f) : + write(w), fd(f) + { + } }; /** @@ -1792,18 +1830,18 @@ struct PrintUsageImplementation template struct StreamWriter: public IStringWriter { - Function* fwrite; - Stream* stream; + Function* fwrite; + Stream* stream; - virtual void operator()(const char* str, int size) - { - (*fwrite)(str, size, 1, stream); - } + virtual void operator()(const char* str, int size) + { + (*fwrite)(str, size, 1, stream); + } - StreamWriter(Function* w, Stream* s) : - fwrite(w), stream(s) - { - } + StreamWriter(Function* w, Stream* s) : + fwrite(w), stream(s) + { + } }; /** @@ -1812,7 +1850,7 @@ struct PrintUsageImplementation */ static void upmax(int& i1, int i2) { - i1 = (i1 >= i2 ? i1 : i2); + i1 = (i1 >= i2 ? i1 : i2); } /** @@ -1828,20 +1866,20 @@ struct PrintUsageImplementation */ static void indent(IStringWriter& write, int& x, int want_x) { - int indent = want_x - x; - if (indent < 0) - { - write("\n", 1); - indent = want_x; - } - - if (indent > 0) - { - char space = ' '; - for (int i = 0; i < indent; ++i) - write(&space, 1); - x = want_x; - } + int indent = want_x - x; + if (indent < 0) + { + write("\n", 1); + indent = want_x; + } + + if (indent > 0) + { + char space = ' '; + for (int i = 0; i < indent; ++i) + write(&space, 1); + x = want_x; + } } /** @@ -1864,13 +1902,13 @@ struct PrintUsageImplementation */ static bool isWideChar(unsigned ch) { - if (ch == 0x303F) - return false; + if (ch == 0x303F) + return false; - return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6) - || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF) - || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6) - || (0x1B000 <= ch)); + return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6) + || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF) + || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6) + || (0x1B000 <= ch)); } /** @@ -1911,253 +1949,253 @@ struct PrintUsageImplementation */ class LinePartIterator { - const Descriptor* tablestart; //!< The 1st descriptor of the current table. - const Descriptor* rowdesc; //!< The Descriptor that contains the current row. - const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help. - const char* ptr; //!< Ptr to current part within the current row. - int col; //!< Index of current column. - int len; //!< Length of the current part (that ptr points at) in BYTES - int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account). - int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs. - int line_in_block; //!< Line index within the current cell of the current part. - int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration. - bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell. - - /** - * @brief Determines the byte and character lengths of the part at @ref ptr and - * stores them in @ref len and @ref screenlen respectively. - */ - void update_length() - { - screenlen = 0; - for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len) - { - ++screenlen; - unsigned ch = (unsigned char) ptr[len]; - if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte - { - // int __builtin_clz (unsigned int x) - // Returns the number of leading 0-bits in x, starting at the most significant bit - unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); - ch = ch & mask; // mask out length bits, we don't verify their correctness - while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte - { - ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code - ++len; - } - // ch is the decoded unicode code point - if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case - ++screenlen; - } - } - } + const Descriptor* tablestart; //!< The 1st descriptor of the current table. + const Descriptor* rowdesc; //!< The Descriptor that contains the current row. + const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help. + const char* ptr; //!< Ptr to current part within the current row. + int col; //!< Index of current column. + int len; //!< Length of the current part (that ptr points at) in BYTES + int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account). + int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs. + int line_in_block; //!< Line index within the current cell of the current part. + int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration. + bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell. + + /** + * @brief Determines the byte and character lengths of the part at @ref ptr and + * stores them in @ref len and @ref screenlen respectively. + */ + void update_length() + { + screenlen = 0; + for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len) + { + ++screenlen; + unsigned ch = (unsigned char) ptr[len]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code + ++len; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + ++screenlen; + } + } + } public: - //! @brief Creates an iterator for @c usage. - LinePartIterator(const Descriptor usage[]) : - tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0), - target_line_in_block(0), hit_target_line(true) - { - } - - /** - * @brief Moves iteration to the next table (if any). Has to be called once on a new - * LinePartIterator to move to the 1st table. - * @retval false if moving to next table failed because no further table exists. - */ - bool nextTable() - { - // If this is NOT the first time nextTable() is called after the constructor, - // then skip to the next table break (i.e. a Descriptor with help == 0) - if (rowdesc != 0) - { - while (tablestart->help != 0 && tablestart->shortopt != 0) - ++tablestart; - } - - // Find the next table after the break (if any) - while (tablestart->help == 0 && tablestart->shortopt != 0) - ++tablestart; - - restartTable(); - return rowstart != 0; - } - - /** - * @brief Reset iteration to the beginning of the current table. - */ - void restartTable() - { - rowdesc = tablestart; - rowstart = tablestart->help; - ptr = 0; - } - - /** - * @brief Moves iteration to the next row (if any). Has to be called once after each call to - * @ref nextTable() to move to the 1st row of the table. - * @retval false if moving to next row failed because no further row exists. - */ - bool nextRow() - { - if (ptr == 0) - { - restartRow(); - return rowstart != 0; - } - - while (*ptr != 0 && *ptr != '\n') - ++ptr; - - if (*ptr == 0) - { - if ((rowdesc + 1)->help == 0) // table break - return false; - - ++rowdesc; - rowstart = rowdesc->help; - } - else // if (*ptr == '\n') - { - rowstart = ptr + 1; - } - - restartRow(); - return true; - } - - /** - * @brief Reset iteration to the beginning of the current row. - */ - void restartRow() - { - ptr = rowstart; - col = -1; - len = 0; - screenlen = 0; - max_line_in_block = 0; - line_in_block = 0; - target_line_in_block = 0; - hit_target_line = true; - } - - /** - * @brief Moves iteration to the next part (if any). Has to be called once after each call to - * @ref nextRow() to move to the 1st part of the row. - * @retval false if moving to next part failed because no further part exists. - * - * See @ref LinePartIterator for details about the iteration. - */ - bool next() - { - if (ptr == 0) - return false; - - if (col == -1) - { - col = 0; - update_length(); - return true; - } - - ptr += len; - while (true) - { - switch (*ptr) - { - case '\v': - upmax(max_line_in_block, ++line_in_block); - ++ptr; - break; - case '\t': - if (!hit_target_line) // if previous column did not have the targetline - { // then "insert" a 0-length part - update_length(); - hit_target_line = true; - return true; - } - - hit_target_line = false; - line_in_block = 0; - ++col; - ++ptr; - break; - case 0: - case '\n': - if (!hit_target_line) // if previous column did not have the targetline - { // then "insert" a 0-length part - update_length(); - hit_target_line = true; - return true; - } - - if (++target_line_in_block > max_line_in_block) - { - update_length(); - return false; - } - - hit_target_line = false; - line_in_block = 0; - col = 0; - ptr = rowstart; - continue; - default: - ++ptr; - continue; - } // switch - - if (line_in_block == target_line_in_block) - { - update_length(); - hit_target_line = true; - return true; - } - } // while - } - - /** - * @brief Returns the index (counting from 0) of the column in which - * the part pointed to by @ref data() is located. - */ - int column() - { - return col; - } - - /** - * @brief Returns the index (counting from 0) of the line within the current column - * this part belongs to. - */ - int line() - { - return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line - } - - /** - * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters). - */ - int length() - { - return len; - } - - /** - * @brief Returns the width in screen columns of the part pointed to by @ref data(). - * Takes multi-byte UTF-8 sequences and wide characters into account. - */ - int screenLength() - { - return screenlen; - } - - /** - * @brief Returns the current part of the iteration. - */ - const char* data() - { - return ptr; - } + //! @brief Creates an iterator for @c usage. + LinePartIterator(const Descriptor usage[]) : + tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0), + target_line_in_block(0), hit_target_line(true) + { + } + + /** + * @brief Moves iteration to the next table (if any). Has to be called once on a new + * LinePartIterator to move to the 1st table. + * @retval false if moving to next table failed because no further table exists. + */ + bool nextTable() + { + // If this is NOT the first time nextTable() is called after the constructor, + // then skip to the next table break (i.e. a Descriptor with help == 0) + if (rowdesc != 0) + { + while (tablestart->help != 0 && tablestart->shortopt != 0) + ++tablestart; + } + + // Find the next table after the break (if any) + while (tablestart->help == 0 && tablestart->shortopt != 0) + ++tablestart; + + restartTable(); + return rowstart != 0; + } + + /** + * @brief Reset iteration to the beginning of the current table. + */ + void restartTable() + { + rowdesc = tablestart; + rowstart = tablestart->help; + ptr = 0; + } + + /** + * @brief Moves iteration to the next row (if any). Has to be called once after each call to + * @ref nextTable() to move to the 1st row of the table. + * @retval false if moving to next row failed because no further row exists. + */ + bool nextRow() + { + if (ptr == 0) + { + restartRow(); + return rowstart != 0; + } + + while (*ptr != 0 && *ptr != '\n') + ++ptr; + + if (*ptr == 0) + { + if ((rowdesc + 1)->help == 0) // table break + return false; + + ++rowdesc; + rowstart = rowdesc->help; + } + else // if (*ptr == '\n') + { + rowstart = ptr + 1; + } + + restartRow(); + return true; + } + + /** + * @brief Reset iteration to the beginning of the current row. + */ + void restartRow() + { + ptr = rowstart; + col = -1; + len = 0; + screenlen = 0; + max_line_in_block = 0; + line_in_block = 0; + target_line_in_block = 0; + hit_target_line = true; + } + + /** + * @brief Moves iteration to the next part (if any). Has to be called once after each call to + * @ref nextRow() to move to the 1st part of the row. + * @retval false if moving to next part failed because no further part exists. + * + * See @ref LinePartIterator for details about the iteration. + */ + bool next() + { + if (ptr == 0) + return false; + + if (col == -1) + { + col = 0; + update_length(); + return true; + } + + ptr += len; + while (true) + { + switch (*ptr) + { + case '\v': + upmax(max_line_in_block, ++line_in_block); + ++ptr; + break; + case '\t': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + hit_target_line = false; + line_in_block = 0; + ++col; + ++ptr; + break; + case 0: + case '\n': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + if (++target_line_in_block > max_line_in_block) + { + update_length(); + return false; + } + + hit_target_line = false; + line_in_block = 0; + col = 0; + ptr = rowstart; + continue; + default: + ++ptr; + continue; + } // switch + + if (line_in_block == target_line_in_block) + { + update_length(); + hit_target_line = true; + return true; + } + } // while + } + + /** + * @brief Returns the index (counting from 0) of the column in which + * the part pointed to by @ref data() is located. + */ + int column() + { + return col; + } + + /** + * @brief Returns the index (counting from 0) of the line within the current column + * this part belongs to. + */ + int line() + { + return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line + } + + /** + * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters). + */ + int length() + { + return len; + } + + /** + * @brief Returns the width in screen columns of the part pointed to by @ref data(). + * Takes multi-byte UTF-8 sequences and wide characters into account. + */ + int screenLength() + { + return screenlen; + } + + /** + * @brief Returns the current part of the iteration. + */ + const char* data() + { + return ptr; + } }; /** @@ -2186,219 +2224,219 @@ struct PrintUsageImplementation */ class LineWrapper { - static const int bufmask = 15; //!< Must be a power of 2 minus 1. - /** - * @brief Ring buffer for length component of pair (data, length). - */ - int lenbuf[bufmask + 1]; - /** - * @brief Ring buffer for data component of pair (data, length). - */ - const char* datbuf[bufmask + 1]; - /** - * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer - * assumes that the indentation has already been written when @ref process() - * is called, so this value is only used when a buffer flush requires writing - * additional lines of output. - */ - int x; - /** - * @brief The width of the column to line wrap. - */ - int width; - int head; //!< @brief index for next write - int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read) - - /** - * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to - * free up space. The contract of process() says that only 1 line is output. So - * this variable is used to track whether something has output a line. It is - * reset at the beginning of process() and checked at the end to decide if - * output has already occurred or is still needed. - */ - bool wrote_something; - - bool buf_empty() - { - return ((tail + 1) & bufmask) == head; - } - - bool buf_full() - { - return tail == head; - } - - void buf_store(const char* data, int len) - { - lenbuf[head] = len; - datbuf[head] = data; - head = (head + 1) & bufmask; - } - - //! @brief Call BEFORE reading ...buf[tail]. - void buf_next() - { - tail = (tail + 1) & bufmask; - } - - /** - * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line - * is flushed out of the buffer into @c write. - */ - void output(IStringWriter& write, const char* data, int len) - { - if (buf_full()) - write_one_line(write); - - buf_store(data, len); - } - - /** - * @brief Writes a single line of output from the buffer to @c write. - */ - void write_one_line(IStringWriter& write) - { - if (wrote_something) // if we already wrote something, we need to start a new line - { - write("\n", 1); - int _ = 0; - indent(write, _, x); - } - - if (!buf_empty()) - { - buf_next(); - write(datbuf[tail], lenbuf[tail]); - } - - wrote_something = true; - } + static const int bufmask = 15; //!< Must be a power of 2 minus 1. + /** + * @brief Ring buffer for length component of pair (data, length). + */ + int lenbuf[bufmask + 1]; + /** + * @brief Ring buffer for data component of pair (data, length). + */ + const char* datbuf[bufmask + 1]; + /** + * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer + * assumes that the indentation has already been written when @ref process() + * is called, so this value is only used when a buffer flush requires writing + * additional lines of output. + */ + int x; + /** + * @brief The width of the column to line wrap. + */ + int width; + int head; //!< @brief index for next write + int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read) + + /** + * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to + * free up space. The contract of process() says that only 1 line is output. So + * this variable is used to track whether something has output a line. It is + * reset at the beginning of process() and checked at the end to decide if + * output has already occurred or is still needed. + */ + bool wrote_something; + + bool buf_empty() + { + return ((tail + 1) & bufmask) == head; + } + + bool buf_full() + { + return tail == head; + } + + void buf_store(const char* data, int len) + { + lenbuf[head] = len; + datbuf[head] = data; + head = (head + 1) & bufmask; + } + + //! @brief Call BEFORE reading ...buf[tail]. + void buf_next() + { + tail = (tail + 1) & bufmask; + } + + /** + * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line + * is flushed out of the buffer into @c write. + */ + void output(IStringWriter& write, const char* data, int len) + { + if (buf_full()) + write_one_line(write); + + buf_store(data, len); + } + + /** + * @brief Writes a single line of output from the buffer to @c write. + */ + void write_one_line(IStringWriter& write) + { + if (wrote_something) // if we already wrote something, we need to start a new line + { + write("\n", 1); + int _ = 0; + indent(write, _, x); + } + + if (!buf_empty()) + { + buf_next(); + write(datbuf[tail], lenbuf[tail]); + } + + wrote_something = true; + } public: - /** - * @brief Writes out all remaining data from the LineWrapper using @c write. - * Unlike @ref process() this method indents all lines including the first and - * will output a \\n at the end (but only if something has been written). - */ - void flush(IStringWriter& write) - { - if (buf_empty()) - return; - int _ = 0; - indent(write, _, x); - wrote_something = false; - while (!buf_empty()) - write_one_line(write); - write("\n", 1); - } - - /** - * @brief Process, wrap and output the next piece of data. - * - * process() will output at least one line of output. This is not necessarily - * the @c data passed in. It may be data queued from a prior call to process(). - * If the internal buffer is full, more than 1 line will be output. - * - * process() assumes that the a proper amount of indentation has already been - * output. It won't write any further indentation before the 1st line. If - * more than 1 line is written due to buffer constraints, the lines following - * the first will be indented by this method, though. - * - * No \\n is written by this method after the last line that is written. - * - * @param write where to write the data. - * @param data the new chunk of data to write. - * @param len the length of the chunk of data to write. - */ - void process(IStringWriter& write, const char* data, int len) - { - wrote_something = false; - - while (len > 0) - { - if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes) - { - output(write, data, len); - len = 0; - } - else // if (len > width) it's possible (but not guaranteed) that utf8len > width - { - int utf8width = 0; - int maxi = 0; - while (maxi < len && utf8width < width) - { - int charbytes = 1; - unsigned ch = (unsigned char) data[maxi]; - if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte - { - // int __builtin_clz (unsigned int x) - // Returns the number of leading 0-bits in x, starting at the most significant bit - unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); - ch = ch & mask; // mask out length bits, we don't verify their correctness - while ((maxi + charbytes < len) && // - (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte - { - ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code - ++charbytes; - } - // ch is the decoded unicode code point - if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case - { - if (utf8width + 2 > width) - break; - ++utf8width; - } - } - ++utf8width; - maxi += charbytes; - } - - // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits - // onto the 1st line. If maxi == len, all characters fit on the line. - - if (maxi == len) - { - output(write, data, len); - len = 0; - } - else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line - { - int i; - for (i = maxi; i >= 0; --i) - if (data[i] == ' ') - break; - - if (i >= 0) - { - output(write, data, i); - data += i + 1; - len -= i + 1; - } - else // did not find a space to split at => split before data[maxi] - { // data[maxi] is always the beginning of a character, never a continuation byte - output(write, data, maxi); - data += maxi; - len -= maxi; - } - } - } - } - if (!wrote_something) // if we didn't already write something to make space in the buffer - write_one_line(write); // write at most one line of actual output - } - - /** - * @brief Constructs a LineWrapper that wraps its output to fit into - * screen columns @c x1 (incl.) to @c x2 (excl.). - * - * @c x1 gives the indentation LineWrapper uses if it needs to indent. - */ - LineWrapper(int x1, int x2) : - x(x1), width(x2 - x1), head(0), tail(bufmask) - { - if (width < 2) // because of wide characters we need at least width 2 or the code breaks - width = 2; - } + /** + * @brief Writes out all remaining data from the LineWrapper using @c write. + * Unlike @ref process() this method indents all lines including the first and + * will output a \\n at the end (but only if something has been written). + */ + void flush(IStringWriter& write) + { + if (buf_empty()) + return; + int _ = 0; + indent(write, _, x); + wrote_something = false; + while (!buf_empty()) + write_one_line(write); + write("\n", 1); + } + + /** + * @brief Process, wrap and output the next piece of data. + * + * process() will output at least one line of output. This is not necessarily + * the @c data passed in. It may be data queued from a prior call to process(). + * If the internal buffer is full, more than 1 line will be output. + * + * process() assumes that the a proper amount of indentation has already been + * output. It won't write any further indentation before the 1st line. If + * more than 1 line is written due to buffer constraints, the lines following + * the first will be indented by this method, though. + * + * No \\n is written by this method after the last line that is written. + * + * @param write where to write the data. + * @param data the new chunk of data to write. + * @param len the length of the chunk of data to write. + */ + void process(IStringWriter& write, const char* data, int len) + { + wrote_something = false; + + while (len > 0) + { + if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes) + { + output(write, data, len); + len = 0; + } + else // if (len > width) it's possible (but not guaranteed) that utf8len > width + { + int utf8width = 0; + int maxi = 0; + while (maxi < len && utf8width < width) + { + int charbytes = 1; + unsigned ch = (unsigned char) data[maxi]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while ((maxi + charbytes < len) && // + (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code + ++charbytes; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + { + if (utf8width + 2 > width) + break; + ++utf8width; + } + } + ++utf8width; + maxi += charbytes; + } + + // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits + // onto the 1st line. If maxi == len, all characters fit on the line. + + if (maxi == len) + { + output(write, data, len); + len = 0; + } + else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line + { + int i; + for (i = maxi; i >= 0; --i) + if (data[i] == ' ') + break; + + if (i >= 0) + { + output(write, data, i); + data += i + 1; + len -= i + 1; + } + else // did not find a space to split at => split before data[maxi] + { // data[maxi] is always the beginning of a character, never a continuation byte + output(write, data, maxi); + data += maxi; + len -= maxi; + } + } + } + } + if (!wrote_something) // if we didn't already write something to make space in the buffer + write_one_line(write); // write at most one line of actual output + } + + /** + * @brief Constructs a LineWrapper that wraps its output to fit into + * screen columns @c x1 (incl.) to @c x2 (excl.). + * + * @c x1 gives the indentation LineWrapper uses if it needs to indent. + */ + LineWrapper(int x1, int x2) : + x(x1), width(x2 - x1), head(0), tail(bufmask) + { + if (width < 2) // because of wide characters we need at least width 2 or the code breaks + width = 2; + } }; /** @@ -2407,166 +2445,170 @@ struct PrintUsageImplementation * Because all printUsage() templates share this implementation, there is no template bloat. */ static void printUsage(IStringWriter& write, const Descriptor usage[], int width = 80, // - int last_column_min_percent = 50, int last_column_own_line_max_percent = 75) - { - if (width < 1) // protect against nonsense values - width = 80; - - if (width > 10000) // protect against overflow in the following computation - width = 10000; - - int last_column_min_width = ((width * last_column_min_percent) + 50) / 100; - int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100; - if (last_column_own_line_max_width == 0) - last_column_own_line_max_width = 1; - - LinePartIterator part(usage); - while (part.nextTable()) - { - - /***************** Determine column widths *******************************/ - - const int maxcolumns = 8; // 8 columns are enough for everyone - int col_width[maxcolumns]; - int lastcolumn; - int leftwidth; - int overlong_column_threshold = 10000; - do - { - lastcolumn = 0; - for (int i = 0; i < maxcolumns; ++i) - col_width[i] = 0; - - part.restartTable(); - while (part.nextRow()) - { - while (part.next()) - { - if (part.column() < maxcolumns) - { - upmax(lastcolumn, part.column()); - if (part.screenLength() < overlong_column_threshold) - // We don't let rows that don't use table separators (\t or \v) influence - // the width of column 0. This allows the user to interject section headers - // or explanatory paragraphs that do not participate in the table layout. - if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' - || part.data()[part.length()] == '\v') - upmax(col_width[part.column()], part.screenLength()); - } - } - } - - /* - * If the last column doesn't fit on the same - * line as the other columns, we can fix that by starting it on its own line. - * However we can't do this for any of the columns 0..lastcolumn-1. - * If their sum exceeds the maximum width we try to fix this by iteratively - * ignoring the widest line parts in the width determination until - * we arrive at a series of column widths that fit into one line. - * The result is a layout where everything is nicely formatted - * except for a few overlong fragments. - * */ - - leftwidth = 0; - overlong_column_threshold = 0; - for (int i = 0; i < lastcolumn; ++i) - { - leftwidth += col_width[i]; - upmax(overlong_column_threshold, col_width[i]); - } - - } while (leftwidth > width); - - /**************** Determine tab stops and last column handling **********************/ - - int tabstop[maxcolumns]; - tabstop[0] = 0; - for (int i = 1; i < maxcolumns; ++i) - tabstop[i] = tabstop[i - 1] + col_width[i - 1]; - - int rightwidth = width - tabstop[lastcolumn]; - bool print_last_column_on_own_line = false; - if (rightwidth < last_column_min_width && rightwidth < col_width[lastcolumn]) - { - print_last_column_on_own_line = true; - rightwidth = last_column_own_line_max_width; - } - - // If lastcolumn == 0 we must disable print_last_column_on_own_line because - // otherwise 2 copies of the last (and only) column would be output. - // Actually this is just defensive programming. It is currently not - // possible that lastcolumn==0 and print_last_column_on_own_line==true - // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 => - // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes - // a bullshit value >100 for last_column_min_percent) => the above if condition - // is false => print_last_column_on_own_line==false - if (lastcolumn == 0) - print_last_column_on_own_line = false; - - LineWrapper lastColumnLineWrapper(width - rightwidth, width); - LineWrapper interjectionLineWrapper(0, width); - - part.restartTable(); - - /***************** Print out all rows of the table *************************************/ - - while (part.nextRow()) - { - int x = -1; - while (part.next()) - { - if (part.column() > lastcolumn) - continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1) - - if (part.column() == 0) - { - if (x >= 0) - write("\n", 1); - x = 0; - } - - indent(write, x, tabstop[part.column()]); - - if ((part.column() < lastcolumn) - && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' - || part.data()[part.length()] == '\v')) - { - write(part.data(), part.length()); - x += part.screenLength(); - } - else // either part.column() == lastcolumn or we are in the special case of - // an interjection that doesn't contain \v or \t - { - // NOTE: This code block is not necessarily executed for - // each line, because some rows may have fewer columns. - - LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper; - - if (!print_last_column_on_own_line) - lineWrapper.process(write, part.data(), part.length()); - } - } // while - - if (print_last_column_on_own_line) - { - part.restartRow(); - while (part.next()) - { - if (part.column() == lastcolumn) - { - write("\n", 1); - int _ = 0; - indent(write, _, width - rightwidth); - lastColumnLineWrapper.process(write, part.data(), part.length()); - } - } - } - - write("\n", 1); - lastColumnLineWrapper.flush(write); - interjectionLineWrapper.flush(write); - } - } + int last_column_min_percent = 50, int last_column_own_line_max_percent = 75) + { + if (width < 1) // protect against nonsense values + width = 80; + + if (width > 10000) // protect against overflow in the following computation + width = 10000; + + int last_column_min_width = ((width * last_column_min_percent) + 50) / 100; + int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100; + if (last_column_own_line_max_width == 0) + last_column_own_line_max_width = 1; + + LinePartIterator part(usage); + while (part.nextTable()) + { + + /***************** Determine column widths *******************************/ + + const int maxcolumns = 8; // 8 columns are enough for everyone + int col_width[maxcolumns]; + int lastcolumn; + int leftwidth; + int overlong_column_threshold = 10000; + do + { + lastcolumn = 0; + for (int i = 0; i < maxcolumns; ++i) + col_width[i] = 0; + + part.restartTable(); + while (part.nextRow()) + { + while (part.next()) + { + if (part.column() < maxcolumns) + { + upmax(lastcolumn, part.column()); + if (part.screenLength() < overlong_column_threshold) + // We don't let rows that don't use table separators (\t or \v) influence + // the width of column 0. This allows the user to interject section headers + // or explanatory paragraphs that do not participate in the table layout. + if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v') + upmax(col_width[part.column()], part.screenLength()); + } + } + } + + /* + * If the last column doesn't fit on the same + * line as the other columns, we can fix that by starting it on its own line. + * However we can't do this for any of the columns 0..lastcolumn-1. + * If their sum exceeds the maximum width we try to fix this by iteratively + * ignoring the widest line parts in the width determination until + * we arrive at a series of column widths that fit into one line. + * The result is a layout where everything is nicely formatted + * except for a few overlong fragments. + * */ + + leftwidth = 0; + overlong_column_threshold = 0; + for (int i = 0; i < lastcolumn; ++i) + { + leftwidth += col_width[i]; + upmax(overlong_column_threshold, col_width[i]); + } + + } while (leftwidth > width); + + /**************** Determine tab stops and last column handling **********************/ + + int tabstop[maxcolumns]; + tabstop[0] = 0; + for (int i = 1; i < maxcolumns; ++i) + tabstop[i] = tabstop[i - 1] + col_width[i - 1]; + + int rightwidth = width - tabstop[lastcolumn]; + bool print_last_column_on_own_line = false; + if (rightwidth < last_column_min_width && // if we don't have the minimum requested width for the last column + ( col_width[lastcolumn] == 0 || // and all last columns are > overlong_column_threshold + rightwidth < col_width[lastcolumn] // or there is at least one last column that requires more than the space available + ) + ) + { + print_last_column_on_own_line = true; + rightwidth = last_column_own_line_max_width; + } + + // If lastcolumn == 0 we must disable print_last_column_on_own_line because + // otherwise 2 copies of the last (and only) column would be output. + // Actually this is just defensive programming. It is currently not + // possible that lastcolumn==0 and print_last_column_on_own_line==true + // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 => + // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes + // a bullshit value >100 for last_column_min_percent) => the above if condition + // is false => print_last_column_on_own_line==false + if (lastcolumn == 0) + print_last_column_on_own_line = false; + + LineWrapper lastColumnLineWrapper(width - rightwidth, width); + LineWrapper interjectionLineWrapper(0, width); + + part.restartTable(); + + /***************** Print out all rows of the table *************************************/ + + while (part.nextRow()) + { + int x = -1; + while (part.next()) + { + if (part.column() > lastcolumn) + continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1) + + if (part.column() == 0) + { + if (x >= 0) + write("\n", 1); + x = 0; + } + + indent(write, x, tabstop[part.column()]); + + if ((part.column() < lastcolumn) + && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v')) + { + write(part.data(), part.length()); + x += part.screenLength(); + } + else // either part.column() == lastcolumn or we are in the special case of + // an interjection that doesn't contain \v or \t + { + // NOTE: This code block is not necessarily executed for + // each line, because some rows may have fewer columns. + + LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper; + + if (!print_last_column_on_own_line || part.column() != lastcolumn) + lineWrapper.process(write, part.data(), part.length()); + } + } // while + + if (print_last_column_on_own_line) + { + part.restartRow(); + while (part.next()) + { + if (part.column() == lastcolumn) + { + write("\n", 1); + int _ = 0; + indent(write, _, width - rightwidth); + lastColumnLineWrapper.process(write, part.data(), part.length()); + } + } + } + + write("\n", 1); + lastColumnLineWrapper.flush(write); + interjectionLineWrapper.flush(write); + } + } } } @@ -2771,7 +2813,7 @@ struct PrintUsageImplementation */ template void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::OStreamWriter write(prn); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2779,7 +2821,7 @@ void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last template void printUsage(Function* prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::FunctionWriter write(prn); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2787,7 +2829,7 @@ void printUsage(Function* prn, const Descriptor usage[], int width = 80, int las template void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::TemporaryWriter write(prn); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2795,7 +2837,7 @@ void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, template void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::SyscallWriter write(prn, fd); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2803,8 +2845,8 @@ void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, template void printUsage(Function* prn, Stream* stream, const Descriptor usage[], int width = 80, int last_column_min_percent = - 50, - int last_column_own_line_max_percent = 75) + 50, + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::StreamWriter write(prn, stream); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); diff --git a/commandLine/src/projects/CBLinuxProject.cpp b/commandLine/src/projects/CBLinuxProject.cpp index 017ed223..734b0747 100644 --- a/commandLine/src/projects/CBLinuxProject.cpp +++ b/commandLine/src/projects/CBLinuxProject.cpp @@ -68,6 +68,7 @@ bool CBLinuxProject::createProjectFile(){ // handle the relative roots. + // FIXME: FS std::string relRoot = getOFRelPath(ofFilePath::removeTrailingSlash(projectDir)); if (relRoot != "../../../"){ std::string relPath2 = relRoot; diff --git a/commandLine/src/projects/CBWinProject.cpp b/commandLine/src/projects/CBWinProject.cpp index 2af82393..5d5e53cc 100644 --- a/commandLine/src/projects/CBWinProject.cpp +++ b/commandLine/src/projects/CBWinProject.cpp @@ -23,6 +23,7 @@ bool CBWinProject::createProjectFile(){ ofFile::copyFromTo(ofFilePath::join(templatePath,"icon.rc"), projectDir / "icon.rc", false, true); //let's do some renaming: + // FIXME: FS std::string relRoot = getOFRelPath(ofFilePath::removeTrailingSlash(projectDir)); if (relRoot != "../../../"){ diff --git a/commandLine/src/projects/baseProject.cpp b/commandLine/src/projects/baseProject.cpp index 4c4bdde1..f3ca2451 100644 --- a/commandLine/src/projects/baseProject.cpp +++ b/commandLine/src/projects/baseProject.cpp @@ -12,34 +12,24 @@ #include "Utils.h" #include "ofConstants.h" #include +#include using std::string; using std::vector; -//using std::cout; -//using std::endl; namespace fs = of::filesystem; +// Temporary +using std::cout; +using std::endl; -const std::string templatesFolder = "scripts/templates/"; +const fs::path templatesFolder = "scripts/templates"; baseProject::baseProject(std::string _target){ bLoaded = false; target = _target; } -std::string baseProject::getPlatformTemplateDir(){ - return ofFilePath::join(getOFRoot(),templatesFolder + target); -} - -void recursiveTemplateCopy(const ofDirectory & templateDir, ofDirectory & projectDir){ - for(auto & f: templateDir){ - if(f.isDirectory()){ - ofDirectory templateSubDir(f.path()); - ofDirectory projectSubDir(ofFilePath::join(projectDir.path(),f.getFileName())); - recursiveTemplateCopy(templateSubDir, projectSubDir); - }else if(f.getFileName()!="template.config"){ - f.copyTo(ofFilePath::join(projectDir.path(),f.getFileName()),false,true); - } - } +fs::path baseProject::getPlatformTemplateDir(){ + return getOFRoot() / templatesFolder / target; } bool isPlatformName(std::string file){ @@ -77,7 +67,7 @@ std::unique_ptr baseProject::parseTemplate(const ofDirect if(platform==target){ supported = true; } - templateConfig->platforms.push_back(platform); + templateConfig->platforms.emplace_back(platform); } }else if(var=="DESCRIPTION"){ templateConfig->description = value; @@ -102,55 +92,68 @@ std::unique_ptr baseProject::parseTemplate(const ofDirect std::vector baseProject::listAvailableTemplates(std::string target){ std::vector templates; - ofDirectory templatesDir(ofFilePath::join(getOFRoot(),templatesFolder)); - for(auto & f: templatesDir.getSorted()){ - if(f.isDirectory()){ - auto templateConfig = parseTemplate(ofDirectory(f)); - if(templateConfig){ - templates.push_back(*templateConfig); - } + std::set sorted; + for (const auto & entry : fs::directory_iterator(getOFRoot() / templatesFolder)) { + auto f = entry.path(); + if (fs::is_directory(f)) { + sorted.insert(f); } } + + for (auto & s : sorted) { + auto templateConfig = parseTemplate(s); + if(templateConfig){ + templates.emplace_back(*templateConfig); + } + } + return templates; } bool baseProject::create(const fs::path & _path, std::string templateName){ + auto path = _path; // just because it is const + templatePath = getPlatformTemplateDir(); addons.clear(); extSrcPaths.clear(); - auto path = _path; - if(!ofFilePath::isAbsolute(path)){ - path = (fs::current_path() / fs::path(path)).string(); + if(!path.is_absolute()){ + path = fs::current_path() / path; } projectDir = path; - - projectName = ofFilePath::getFileName(path); + projectName = path.parent_path().filename(); bool bDoesDirExist = false; - ofDirectory project(ofFilePath::join(projectDir,"src")); // this is a directory, really? - if(project.exists()){ + fs::path project { projectDir / "src" }; + if (fs::exists(project) && fs::is_directory(project)) { bDoesDirExist = true; }else{ - ofDirectory project(projectDir); - ofDirectory(ofFilePath::join(templatePath,"src")).copyTo(ofFilePath::join(projectDir,"src")); - ofDirectory(ofFilePath::join(templatePath,"bin")).copyTo(ofFilePath::join(projectDir,"bin")); + // MARK: ofDirectory? +// bool copyTo(const of::filesystem::path& path, bool bRelativeToData = true, bool overwrite = false); + ofDirectory(templatePath / "src").copyTo(projectDir / "src"); + ofDirectory(templatePath / "bin").copyTo(projectDir / "bin"); } bool ret = createProjectFile(); if(!ret) return false; + //MARK: - if(templateName!=""){ - auto name = ofFilePath::join(getOFRoot(),templatesFolder + templateName); - ofDirectory templateDir(ofFilePath::join(getOFRoot(),templatesFolder + templateName)); - templateDir.setShowHidden(true); + fs::path templateDir = getOFRoot() / templatesFolder / templateName; + + // TODO: PORT +// !!!: asdf +// templateDir.setShowHidden(true); auto templateConfig = parseTemplate(templateDir); if(templateConfig){ - ofDirectory project(projectDir); - recursiveTemplateCopy(templateDir,project); + recursiveTemplateCopy(templateDir, projectDir); for(auto & rename: templateConfig->renames){ - auto from = (projectDir / rename.first).string(); - auto to = (projectDir / rename.second).string(); + + auto from = projectDir / rename.first; + auto to = projectDir / rename.second; +// auto to = projectDir / templateConfig->renames[rename.first]; + +// moveTo(const of::filesystem::path& path, bool bRelativeToData = true, bool overwrite = false); ofFile(from).moveTo(to,true,true); } }else{ @@ -179,10 +182,8 @@ bool baseProject::create(const fs::path & _path, std::string templateName){ fileName != "src/main.cpp" && fileName != "src/ofApp.mm" && fileName != "src/main.mm") { -// cout << "add filename:: " << rel << " :: " << folder << endl; addSrc(rel.string(), folder.string()); } else { -// cout << "not adding filename:: " << rel << " :: " << folder << endl; } } @@ -249,47 +250,33 @@ bool baseProject::save(){ } bool baseProject::isAddonInCache(const std::string & addonPath, const std::string platform){ - auto it = addonsCache.find(platform); - if (it == addonsCache.end()) return false; - auto it2 = it->second.find(addonPath); - return it2 != it->second.end(); + if (addonsCache.find(platform) == addonsCache.end()) return false; + return addonsCache[platform].find(addonPath) != addonsCache[platform].end(); } -using std::cout; -using std::endl; void baseProject::addAddon(std::string addonName){ ofAddon addon; -// cout << projectDir << endl; addon.pathToOF = getOFRelPath(projectDir.string()); addon.pathToProject = ofFilePath::getAbsolutePath(projectDir); bool addonOK = false; - bool inCache = isAddonInCache(addonName, target); - //inCache = false; //to test no-cache scenario - - if (fs::exists(addonName)) { + + fs::path addonPath { addonName }; + if (fs::exists(addonPath)) { addon.isLocalAddon = true; - if(!inCache){ - - addonOK = addon.fromFS(addonName, target); - }else{ - addon = addonsCache[target][addonName]; - addonOK = true; - } + } else { + addonPath = fs::path(getOFRoot()) / "addons" / addonName; + addon.isLocalAddon = false; } - - else{ - addon.isLocalAddon = false; - auto standardPath = ofFilePath::join(ofFilePath::join(getOFRoot(), "addons"), addonName); - if(!inCache){ - addonOK = addon.fromFS(standardPath, target); - }else{ - addon = addonsCache[target][addonName]; - addonOK = true; - } + if(!inCache){ + addonOK = addon.fromFS(addonPath, target); + }else{ + addon = addonsCache[target][addonName]; + addonOK = true; } + if(!addonOK){ ofLogVerbose() << "Ignoring addon that doesn't seem to exist: " << addonName; return; //if addon does not exist, stop early @@ -298,31 +285,38 @@ void baseProject::addAddon(std::string addonName){ if(!inCache){ addonsCache[target][addonName] = addon; //cache the addon so we dont have to be reading form disk all the time } + addAddon(addon); // Process values from ADDON_DATA if(addon.data.size()){ - - for(auto& d : addon.data){ - - fs::path path(ofFilePath::join(addon.addonPath, d)); - + for(auto & data : addon.data){ + std::string d = data; + ofStringReplace(d, "data/", ""); // avoid to copy files at /data/data/* + + fs::path path { addon.addonPath / data }; + fs::path dest { projectDir / "bin" / "data" }; + + if (addon.isLocalAddon) { + path = addon.pathToProject / path; + } + if(fs::exists(path)){ if (fs::is_regular_file(path)){ - ofFile src({path}); - string dest = ofFilePath::join(projectDir, "bin/data/"); - ofStringReplace(d, "data/", ""); // avoid to copy files at /data/data/* - bool success = src.copyTo(ofFilePath::join(dest, d), false, true); + // TODO: FS + ofFile src(path); + // bool copyTo(const of::filesystem::path& path, bool bRelativeToData = true, bool overwrite = false) const; + bool success = src.copyTo(dest / d, false, true); if(success){ ofLogVerbose() << "adding addon data file: " << d; }else { ofLogWarning() << "Can not add addon data file: " << d; } }else if(fs::is_directory(path)){ - ofDirectory dir({path}); - string dest = ofFilePath::join(projectDir, "bin/data/"); - ofStringReplace(d, "data/", ""); // avoid to copy files at /data/data/* - bool success = dir.copyTo(ofFilePath::join(dest, d), false, true); + // TODO: FS + ofDirectory dir(path); +// bool copyTo(const of::filesystem::path& path, bool bRelativeToData = true, bool overwrite = false); + bool success = dir.copyTo(dest / d, false, true); if(success){ ofLogVerbose() << "adding addon data folder: " << d; }else{ @@ -336,8 +330,9 @@ void baseProject::addAddon(std::string addonName){ } } +// FIXME: FS parameter void baseProject::addSrcRecursively(std::string srcPath){ - extSrcPaths.push_back(srcPath); + extSrcPaths.emplace_back(srcPath); vector < string > srcFilesToAdd; //so we can just pass through the file paths @@ -356,7 +351,8 @@ void baseProject::addSrcRecursively(std::string srcPath){ //we want folders added for shared_of_code/ and any subfolders, but not folders added for /user/ /user/person/ etc string parentFolder = ofFilePath::getEnclosingDirectory(ofFilePath::removeTrailingSlash(srcPath)); - std::map uniqueIncludeFolders; + // FIXME: - I've inspected this map and it is kinda silly because the key is always equal to the value (first = second) + std::unordered_map uniqueIncludeFolders; for( auto & fileToAdd : srcFilesToAdd){ // cout << "fileToAdd :: " << fileToAdd << endl; //if it is an absolute path it is easy - add the file and enclosing folder to the project @@ -378,7 +374,6 @@ void baseProject::addSrcRecursively(std::string srcPath){ addSrc(fileToAdd, folder); uniqueIncludeFolders[absFolder] = absFolder; }else{ - auto absPath = fileToAdd; //if it is a realtive path make the file relative to the project folder @@ -406,7 +401,7 @@ void baseProject::addSrcRecursively(std::string srcPath){ addSrc(relPathPathToAdd, folder); uniqueIncludeFolders[includeFolder] = includeFolder; } - } + } //do it this way so we don't try and add a include folder for each file ( as it checks if they are already added ) so should be faster for(auto & includeFolder : uniqueIncludeFolders){ @@ -417,25 +412,34 @@ void baseProject::addSrcRecursively(std::string srcPath){ } void baseProject::addAddon(ofAddon & addon){ - for(int i=0;i<(int)addons.size();i++){ if(addons[i].name==addon.name){ return; } } - for(int i=0;i dependencies" << endl; + for (auto & d : addon.dependencies) { + bool found = false; + for (auto & a : addons) { + if (a.name == d) { + found = true; + break; } } + if (!found) { + alert(">>>> addaddon :: " + d, 35); + addAddon(d); + } else { + ofLogVerbose() << "trying to add duplicated addon dependency! skipping: " << d; + } } + cout << "---> " << endl; - - addons.push_back(addon); + addons.emplace_back(addon); ofLogVerbose("baseProject") << "libs in addAddon " << addon.libs.size(); for(auto & lib: addon.libs){ @@ -464,23 +468,23 @@ void baseProject::addAddon(ofAddon & addon){ } for(int i=0;i<(int)addon.srcFiles.size(); i++){ ofLogVerbose() << "adding addon srcFiles: " << addon.srcFiles[i]; - addSrc(addon.srcFiles[i],addon.filesToFolders[addon.srcFiles[i]]); + addSrc(addon.srcFiles[i], addon.filesToFolders[addon.srcFiles[i]]); } for(int i=0;i<(int)addon.csrcFiles.size(); i++){ ofLogVerbose() << "adding addon c srcFiles: " << addon.csrcFiles[i]; - addSrc(addon.csrcFiles[i],addon.filesToFolders[addon.csrcFiles[i]],C); + addSrc(addon.csrcFiles[i], addon.filesToFolders[addon.csrcFiles[i]],C); } for(int i=0;i<(int)addon.cppsrcFiles.size(); i++){ ofLogVerbose() << "adding addon cpp srcFiles: " << addon.cppsrcFiles[i]; - addSrc(addon.cppsrcFiles[i],addon.filesToFolders[addon.cppsrcFiles[i]],CPP); + addSrc(addon.cppsrcFiles[i], addon.filesToFolders[addon.cppsrcFiles[i]],CPP); } for(int i=0;i<(int)addon.objcsrcFiles.size(); i++){ ofLogVerbose() << "adding addon objc srcFiles: " << addon.objcsrcFiles[i]; - addSrc(addon.objcsrcFiles[i],addon.filesToFolders[addon.objcsrcFiles[i]],OBJC); + addSrc(addon.objcsrcFiles[i], addon.filesToFolders[addon.objcsrcFiles[i]],OBJC); } for(int i=0;i<(int)addon.headersrcFiles.size(); i++){ ofLogVerbose() << "adding addon header srcFiles: " << addon.headersrcFiles[i]; - addSrc(addon.headersrcFiles[i],addon.filesToFolders[addon.headersrcFiles[i]],HEADER); + addSrc(addon.headersrcFiles[i], addon.filesToFolders[addon.headersrcFiles[i]],HEADER); } for (int i = 0; i<(int)addon.defines.size(); i++) { ofLogVerbose() << "adding addon defines: " << addon.defines[i]; @@ -489,6 +493,8 @@ void baseProject::addAddon(ofAddon & addon){ } void baseProject::parseAddons(){ + alert("--- parseAddons"); + ofFile addonsMake(ofFilePath::join(projectDir,"addons.make")); ofBuffer addonsMakeMem; addonsMake >> addonsMakeMem; @@ -498,6 +504,8 @@ void baseProject::parseAddons(){ if(addon == "") continue; addAddon(ofSplitString(addon, "#")[0]); } + alert("--- end parseAddons"); + } void baseProject::parseConfigMake(){ @@ -519,18 +527,44 @@ void baseProject::parseConfigMake(){ } } } - } -void baseProject::recursiveCopyContents(const ofDirectory & srcDir, ofDirectory & destDir){ - for(auto & f: srcDir){ - if(f.isDirectory()){ - ofDirectory srcSubDir(f.path()); - ofDirectory destSubDir(ofFilePath::join(destDir.path(),f.getFileName())); - recursiveTemplateCopy(srcSubDir, destSubDir); - }else{ - f.copyTo(ofFilePath::join(destDir.path(),f.getFileName()),false,true); +void baseProject::recursiveTemplateCopy(const fs::path & srcDir, const fs::path & destDir){ + for (const auto & entry : fs::directory_iterator(srcDir)) { + auto f = entry.path(); + auto destFile = destDir / f.filename(); + if (fs::is_directory(f)) { + recursiveTemplateCopy(f, destFile); + } + else if (f.filename() != "template.config") { + if (!fs::exists(destFile)) { + fs::copy_file(f, destFile); // from, to + } } } } +void baseProject::recursiveCopyContents(const fs::path & srcDir, const fs::path & destDir){ + for (const auto & entry : fs::directory_iterator(srcDir)) { + auto f = entry.path(); + auto destFile = destDir / f.filename(); + if (fs::is_directory(f)) { + recursiveTemplateCopy(f, destFile); + } else { + if (!fs::exists(destFile)) { + fs::copy_file(f, destFile); + } + } + } + +//void baseProject::recursiveCopyContents(const ofDirectory & srcDir, ofDirectory & destDir){ +// for(auto & f: srcDir){ +// if(f.isDirectory()){ +// ofDirectory srcSubDir(f.path()); +// ofDirectory destSubDir(ofFilePath::join(destDir.path(),f.getFileName())); +// recursiveTemplateCopy(srcSubDir, destSubDir); +// }else{ +// f.copyTo(ofFilePath::join(destDir.path(),f.getFileName()),false,true); +// } +// } +} diff --git a/commandLine/src/projects/baseProject.h b/commandLine/src/projects/baseProject.h index ceb076d8..c99beaae 100644 --- a/commandLine/src/projects/baseProject.h +++ b/commandLine/src/projects/baseProject.h @@ -6,6 +6,9 @@ #include "ofConstants.h" #include "ofFileUtils.h" #include "pugixml.hpp" +// TODO: - Remove +#include +namespace fs = of::filesystem; class baseProject { @@ -27,8 +30,9 @@ class baseProject { struct Template{ ofDirectory dir; std::string name; - std::vector platforms; + std::vector platforms; std::string description; +// std::unordered_map renames; std::map renames; bool operator<(const Template & other) const{ return dir listAvailableTemplates(std::string target); std::unique_ptr parseTemplate(const ofDirectory & templateDir); - virtual std::string getPlatformTemplateDir(); + virtual fs::path getPlatformTemplateDir(); pugi::xml_document doc; bool bLoaded; - of::filesystem::path projectDir; + fs::path projectDir; + fs::path templatePath; std::string projectName; - std::string templatePath; std::string target; protected: - void recursiveCopyContents(const ofDirectory & srcDir, ofDirectory & destDir); - + void recursiveCopyContents(const fs::path & srcDir, const fs::path & destDir); + void recursiveTemplateCopy(const fs::path & srcDir, const fs::path & destDir); + std::vector addons; std::vector extSrcPaths; //cached addons - if an addon is requested more than once, avoid loading from disk as it's quite slow - std::map> addonsCache; //indexed by [platform][supplied path] + std::unordered_map> addonsCache; //indexed by [platform][supplied path] bool isAddonInCache(const std::string & addonPath, const std::string platform); //is this addon in the mem cache? }; diff --git a/commandLine/src/projects/qtcreatorproject.cpp b/commandLine/src/projects/qtcreatorproject.cpp index f4184b75..93e851cc 100644 --- a/commandLine/src/projects/qtcreatorproject.cpp +++ b/commandLine/src/projects/qtcreatorproject.cpp @@ -50,6 +50,7 @@ bool QtCreatorProject::createProjectFile(){ // handle the relative roots. + // FIXME: FS std::string relRoot = getOFRelPath(ofFilePath::removeTrailingSlash(projectDir)); if (relRoot != "../../../"){ std::string relPath2 = relRoot; @@ -149,5 +150,5 @@ void QtCreatorProject::addAddon(ofAddon & addon){ if(addons[i].name==addon.name) return; } - addons.push_back(addon); + addons.emplace_back(addon); } diff --git a/commandLine/src/projects/visualStudioProject.cpp b/commandLine/src/projects/visualStudioProject.cpp index 7cb03ef4..7c9ea004 100644 --- a/commandLine/src/projects/visualStudioProject.cpp +++ b/commandLine/src/projects/visualStudioProject.cpp @@ -30,6 +30,7 @@ bool visualStudioProject::createProjectFile(){ findandreplaceInTexfile(user,"emptyExample",projectName); findandreplaceInTexfile(project,"emptyExample",projectName); + // FIXME: FS std::string relRoot = getOFRelPath(projectDir.string()); if (relRoot != "../../../"){ @@ -218,7 +219,7 @@ void visualStudioProject::addInclude(std::string includeName){ } } if (bAdd == true){ - strings.push_back(includeName); + strings.emplace_back(includeName); std::string includesNew = unsplitString(strings, ";"); node.node().first_child().set_value(includesNew.c_str()); } @@ -238,7 +239,7 @@ void addLibraryPath(const pugi::xpath_node_set & nodes, std::string libFolder) { } } if (bAdd == true) { - strings.push_back(libFolder); + strings.emplace_back(libFolder); std::string libPathsNew = unsplitString(strings, ";"); node.node().first_child().set_value(libPathsNew.c_str()); } @@ -259,7 +260,7 @@ void addLibraryName(const pugi::xpath_node_set & nodes, std::string libName) { } if (bAdd == true) { - strings.push_back(libName); + strings.emplace_back(libName); std::string libsNew = unsplitString(strings, ";"); node.node().first_child().set_value(libsNew.c_str()); } @@ -399,7 +400,7 @@ void visualStudioProject::addAddon(ofAddon & addon){ } ofLogNotice() << "adding addon: " << addon.name; - addons.push_back(addon); + addons.emplace_back(addon); for(int i=0;i<(int)addon.includePaths.size();i++){ ofLogVerbose() << "adding addon include path: " << addon.includePaths[i]; diff --git a/commandLine/src/projects/xcodeProject.cpp b/commandLine/src/projects/xcodeProject.cpp index a593f55e..7c6c7df8 100644 --- a/commandLine/src/projects/xcodeProject.cpp +++ b/commandLine/src/projects/xcodeProject.cpp @@ -63,9 +63,8 @@ xcodeProject::xcodeProject(string target) }; bool xcodeProject::createProjectFile(){ - // cout << "createProjectFile() xcodeProject " << xcodeProject << endl; fs::path xcodeProject = projectDir / ( projectName + ".xcodeproj" ); - + if (ofDirectory::doesDirectoryExist(xcodeProject)){ ofDirectory::removeDirectory(xcodeProject, true); } @@ -88,19 +87,28 @@ bool xcodeProject::createProjectFile(){ dataDirectory.close(); } if(binDirectory.exists()){ - ofDirectory dataDirectory(ofFilePath::join(binDirectory.path(), "data")); - if (!dataDirectory.exists()){ - dataDirectory.create(false); + fs::path dataDirectory { fs::path(binDirectory.path()) / "data" }; + if (!fs::exists(dataDirectory)) { + fs::create_directory(dataDirectory); } // originally only on IOS //this is needed for 0.9.3 / 0.9.4 projects which have iOS media assets in bin/data/ - ofDirectory srcDataDir(ofFilePath::join(templatePath, "bin/data")); - if( srcDataDir.exists() ){ + fs::path srcDataDir { fs::path{ templatePath } / "bin" / "data" }; + if (fs::exists(srcDataDir) && fs::is_directory(srcDataDir)) { baseProject::recursiveCopyContents(srcDataDir, dataDirectory); } - dataDirectory.close(); - srcDataDir.close(); + // ofDirectory dataDirectory(ofFilePath::join(binDirectory.path(), "data")); + // if (!dataDirectory.exists()){ + // dataDirectory.create(false); + // } + +// ofDirectory srcDataDir(ofFilePath::join(templatePath, "bin/data")); +// if( srcDataDir.exists() ){ +// baseProject::recursiveCopyContents(srcDataDir, dataDirectory); +// } +// dataDirectory.close(); +// srcDataDir.close(); } binDirectory.close(); @@ -128,9 +136,10 @@ bool xcodeProject::createProjectFile(){ } // make everything relative the right way. + // FIXME: FS relRoot = getOFRelPathFS(projectDir).string(); projectDir = projectDir.lexically_normal(); - + //projectDir is always absolute at the moment //so lets check if the projectDir is inside the OF folder - if it is not make the OF path absolute if( projectDir.string().rfind(getOFRoot(),0) != 0 ){ @@ -143,7 +152,7 @@ bool xcodeProject::createProjectFile(){ findandreplaceInTexfile(projectDir / "Makefile", "../../..", relRoot); findandreplaceInTexfile(projectDir / "config.make", "../../..", relRoot); } - } + } return true; } @@ -194,6 +203,7 @@ void xcodeProject::renameProject(){ //base // Just OSX here, debug app naming. if( target == "osx" ){ // TODO: Hardcode to variable + // FIXME: Debug needed in name? commands.emplace_back("Set :objects:E4B69B5B0A3A1756003C02F2:path " + projectName + "Debug.app"); } } @@ -428,7 +438,7 @@ void xcodeProject::addSrc(string srcFile, string folder, SrcType type){ } void xcodeProject::addFramework(string name, string path, string folder){ -// cout << "addFramework " << name << " path = " << path << " folder = " << folder << endl; + cout << "addFramework " << name << " path = " << path << " folder = " << folder << endl; // name = name of the framework // path = the full path (w name) of this framework // folder = the path in the addon (in case we want to add this to the file browser -- we don't do that for system libs); @@ -459,8 +469,7 @@ void xcodeProject::addFramework(string name, string path, string folder){ commands.emplace_back("Add :objects:"+buildUUID+":settings:ATTRIBUTES array"); commands.emplace_back("Add :objects:"+buildUUID+":settings:ATTRIBUTES: string CodeSignOnCopy"); - // we add one of the build refs to the list of frameworks - // TENTATIVA desesperada aqui... + // this now adds the recently created object UUID to its parent folder string folderUUID = getFolderUUID(folder, false); commands.emplace_back("Add :objects:"+folderUUID+":children: string " + UUID); @@ -518,6 +527,7 @@ void xcodeProject::addFramework(string name, string path, string folder){ } void xcodeProject::addDylib(string name, string path){ +// alert("addDylib " + name + " : " + path); // name = name of the dylib // path = the full path (w name) of this framework // folder = the path in the addon (in case we want to add this to the file browser -- we don't do that for system libs); @@ -540,6 +550,13 @@ void xcodeProject::addDylib(string name, string path){ commands.emplace_back("Add :objects:"+UUID+":lastKnownFileType string compiled.mach-o.dylib"); commands.emplace_back("Add :objects:"+UUID+":sourceTree string SOURCE_ROOT"); + + fs::path fsPath { path }; + fs::path folder = fsPath.parent_path(); + string folderUUID = getFolderUUID(folder.string(), false); + commands.emplace_back("Add :objects:"+folderUUID+":children: string " + UUID); + + string buildUUID = generateUUID(name + "-build"); commands.emplace_back("Add :objects:"+buildUUID+":isa string PBXBuildFile"); commands.emplace_back("Add :objects:"+buildUUID+":fileRef string "+UUID); @@ -552,6 +569,10 @@ void xcodeProject::addDylib(string name, string path){ // // TENTATIVA desesperada aqui... // string folderUUID = getFolderUUID(folder); // commands.emplace_back("Add :objects:"+folderUUID+":children: string " + UUID); + +// string folderUUID = getFolderUUID(folder, false); +// commands.emplace_back("Add :objects:"+folderUUID+":children: string " + UUID); + string buildUUID2 = generateUUID(name + "-build2"); commands.emplace_back("Add :objects:"+buildUUID2+":fileRef string "+UUID); @@ -564,17 +585,15 @@ void xcodeProject::addDylib(string name, string path){ // UUID hardcoded para PBXCopyFilesBuildPhase // FIXME: hardcoded - this is the same for the next fixme. so maybe a clearer ident can make things better here. commands.emplace_back("Add :objects:E4A5B60F29BAAAE400C2D356:files: string " + buildUUID2); - } void xcodeProject::addInclude(string includeName){ +// alert("addInclude " + includeName); -// string relRoot = getOFRelPathFS(projectDir).string(); -// ofStringReplace(includeName, relRoot, "$(OF_PATH)"); - - // Adding source to all build configurations, debug and release - for (auto & c : buildConfigurations) { + // Adding to all build configurations, debug and release +// for (auto & c : buildConfigurations) { + for (auto & c : buildConfigs) { string s = "Add :objects:"+c+":buildSettings:HEADER_SEARCH_PATHS: string " + includeName; commands.emplace_back("Add :objects:"+c+":buildSettings:HEADER_SEARCH_PATHS: string " + includeName); } @@ -595,7 +614,8 @@ void xcodeProject::addLDFLAG(string ldflag, LibType libType){ } void xcodeProject::addCFLAG(string cflag, LibType libType){ - for (auto & c : buildConfigurations) { +// for (auto & c : buildConfigurations) { + for (auto & c : buildConfigs) { // FIXME: add array here if it doesnt exist commands.emplace_back("Add :objects:"+c+":buildSettings:OTHER_CFLAGS array"); commands.emplace_back("Add :objects:"+c+":buildSettings:OTHER_CFLAGS: string " + cflag); @@ -634,29 +654,30 @@ void xcodeProject::addAfterRule(string rule){ } void xcodeProject::addAddon(ofAddon & addon){ - - for(int i=0;i<(int)addons.size();i++){ - if(addons[i].name==addon.name){ - return; - } - } - - for(int i=0;i cols = ofSplitString(c, " "); - string thispath = cols[1]; - ofStringReplace(thispath, ":", "/"); - - if (thispath.substr(thispath.length() -1) != "/") { - //if (cols[0] == "Set") { - json::json_pointer p = json::json_pointer(thispath); - if (cols[2] == "string") { - j[p] = cols[3]; - } - else if (cols[2] == "array") { - j[p] = {}; - } + bool usePlistBuddy = false; + + if (usePlistBuddy) { + // PLISTBUDDY - Mac only + string command = "/usr/libexec/PlistBuddy " + fileName.string(); + string allCommands = ""; + for (auto & c : commands) { + command += " -c \"" + c + "\""; + allCommands += c + "\n"; } - else { - thispath = thispath.substr(0, thispath.length() -1); - json::json_pointer p = json::json_pointer(thispath); - try { - // Fixing XCode one item array issue - if (!j[p].is_array()) { - auto v = j[p]; - j[p] = json::array(); - if (!v.is_null()) { - j[p].push_back(v); + cout << ofSystem(command) << endl; + } else { + // JSON Block - Multiplatform + string contents = ofBufferFromFile(fileName).getText(); + json j = json::parse(contents); + + for (auto & c : commands) { + // cout << c << endl; + vector cols = ofSplitString(c, " "); + string thispath = cols[1]; + ofStringReplace(thispath, ":", "/"); + + if (thispath.substr(thispath.length() -1) != "/") { + //if (cols[0] == "Set") { + json::json_pointer p = json::json_pointer(thispath); + if (cols[2] == "string") { + j[p] = cols[3]; + } + else if (cols[2] == "array") { + j[p] = {}; + } + } + else { + thispath = thispath.substr(0, thispath.length() -1); + json::json_pointer p = json::json_pointer(thispath); + try { + // Fixing XCode one item array issue + if (!j[p].is_array()) { + auto v = j[p]; + j[p] = json::array(); + if (!v.is_null()) { + j[p].emplace_back(v); + } } + j[p].emplace_back(cols[3]); + + } catch (std::exception e) { + cout << "json error " << endl; + cout << e.what() << endl; } - j[p].push_back(cols[3]); - - } catch (std::exception e) { - cout << "json error " << endl; - cout << e.what() << endl; } } - } - - ofFile jsonFile(fileName, ofFile::WriteOnly); - try{ - jsonFile << j.dump(1, ' '); - }catch(std::exception & e){ - ofLogError("ofSaveJson") << "Error saving json to " << fileName << ": " << e.what(); - return false; - }catch(...){ - ofLogError("ofSaveJson") << "Error saving json to " << fileName; - return false; + + ofFile jsonFile(fileName, ofFile::WriteOnly); + try{ + jsonFile << j.dump(1, ' '); + }catch(std::exception & e){ + ofLogError("ofSaveJson") << "Error saving json to " << fileName << ": " << e.what(); + return false; + }catch(...){ + ofLogError("ofSaveJson") << "Error saving json to " << fileName; + return false; + } } // for (auto & c : commands) { // cout << c << endl; // } - - // PLISTBUDDY - Mac only -// { -// string command = "/usr/libexec/PlistBuddy " + fileName; -// string allCommands = ""; -// for (auto & c : commands) { -// command += " -c \"" + c + "\""; -// allCommands += c + "\n"; -// } -// cout << ofSystem(command) << endl; -// cout << allCommands << endl; -// } return true; } diff --git a/commandLine/src/projects/xcodeProject.h b/commandLine/src/projects/xcodeProject.h index 39cd7209..e7166ced 100644 --- a/commandLine/src/projects/xcodeProject.h +++ b/commandLine/src/projects/xcodeProject.h @@ -49,6 +49,8 @@ class xcodeProject : public baseProject { std::vector commands; + // MARK: now this is not being used anymore, only buildConfigs + // TODO: test in iOS and clean if OK string buildConfigurations[4] = { "E4B69B600A3A1757003C02F2", //macOS Debug "E4B69B610A3A1757003C02F2", //macOS Release diff --git a/commandLine/src/utils/LibraryBinary.h b/commandLine/src/utils/LibraryBinary.h index 5b5ebf4e..545a7efc 100644 --- a/commandLine/src/utils/LibraryBinary.h +++ b/commandLine/src/utils/LibraryBinary.h @@ -4,10 +4,11 @@ #include struct LibraryBinary { + // FIXME: FS (and includes) std::string path; std::string arch; std::string target; static const std::vector archs; static const std::vector targets; -}; \ No newline at end of file +}; diff --git a/commandLine/src/utils/Utils.cpp b/commandLine/src/utils/Utils.cpp index 7f888f68..bdd30300 100644 --- a/commandLine/src/utils/Utils.cpp +++ b/commandLine/src/utils/Utils.cpp @@ -32,6 +32,9 @@ using std::unique_ptr; namespace fs = of::filesystem; +using std::cout; +using std::endl; + std::string generateUUID(std::string input){ return uuidxx::uuid::Generate().ToString(); } @@ -53,12 +56,10 @@ void findandreplace( std::string& tInput, std::string tFind, std::string tReplac } -std::string LoadFileAsString(const std::string & fn) -{ +std::string LoadFileAsString(const std::string & fn) { std::ifstream fin(fn.c_str()); - if(!fin) - { + if(!fin) { // throw exception } @@ -82,22 +83,7 @@ void findandreplaceInTexfile (const fs::path & fileName, std::string tFind, std: myfile << bufferStr; myfile.close(); - /* - std::ifstream ifile(ofToDataPath(fileName).c_str(),std::ios::binary); - ifile.seekg(0,std::ios_base::end); - long s=ifile.tellg(); - char *buffer=new char[s]; - ifile.seekg(0); - ifile.read(buffer,s); - ifile.close(); - std::string txt(buffer,s); - delete[] buffer; - findandreplace(txt, tFind, tReplace); - std::ofstream ofile(ofToDataPath(fileName).c_str()); - ofile.write(txt.c_str(),txt.size()); - */ - //return 0; - } else { + } else { ; // some error checking here would be good. } } @@ -147,37 +133,38 @@ pugi::xml_node appendValue(pugi::xml_document & doc, std::string tag, std::strin } -// todo -- this doesn't use ofToDataPath -- so it's broken a bit. can we fix? + void getFilesRecursively(const fs::path & path, std::vector < string > & fileNames){ - // cout << "---- getFilesRecursively :: " << path << endl; - + if (!fs::exists(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console + if (!fs::is_directory(path)) return; for (const auto & entry : fs::directory_iterator(path)) { auto f = entry.path(); + if (f.filename().c_str()[0] == '.') continue; // avoid hidden files .DS_Store .vscode .git etc if (ofIsStringInString(f.filename().string(),".framework")) continue; // ignore frameworks if (fs::is_directory(f)) { - if (f.filename() != fs::path(".git")) { // ignore git dir - getFilesRecursively(f, fileNames); - } - } - else { + getFilesRecursively(f, fileNames); + } else { // FIXME - update someday to fs::path - fileNames.emplace_back(f.string()); + fileNames.emplace_back(f); } } } + void getFilesRecursively(const fs::path & path, std::vector < fs::path > & fileNames){ + if (!fs::exists(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console + if (!fs::is_directory(path)) return; for (const auto & entry : fs::directory_iterator(path)) { auto f = entry.path(); + if (f.filename().c_str()[0] == '.') continue; // avoid hidden files .DS_Store .vscode .git etc if (ofIsStringInString(f.filename().string(),".framework")) continue; // ignore frameworks if (fs::is_directory(f)) { if (f.filename() != fs::path(".git")) { // ignore git dir getFilesRecursively(f, fileNames); } - } - else { + } else { fileNames.emplace_back(f); } } @@ -187,14 +174,16 @@ void getFilesRecursively(const fs::path & path, std::vector < fs::path > & fileN static std::vector platforms; bool isFolderNotCurrentPlatform(std::string folderName, std::string platform){ if( platforms.size() == 0 ){ - platforms.push_back("osx"); - platforms.push_back("msys2"); - platforms.push_back("vs"); - platforms.push_back("ios"); - platforms.push_back("linux"); - platforms.push_back("linux64"); - platforms.push_back("android"); - platforms.push_back("iphone"); + platforms = { + "osx", + "msys2", + "vs", + "ios", + "linux", + "linux64", + "android", + "iphone", + }; } for(int i = 0; i < platforms.size(); i++){ @@ -220,123 +209,92 @@ void splitFromFirst(std::string toSplit, std::string deliminator, std::string & void getFoldersRecursively(const fs::path & path, std::vector < std::string > & folderNames, std::string platform){ - ofDirectory dir; - - if (!ofIsStringInString(path.string(), ".framework")){ - dir.listDir(path); - for (int i = 0; i < dir.size(); i++){ - ofFile temp(dir.getFile(i)); - if (temp.isDirectory() && isFolderNotCurrentPlatform(temp.getFileName(), platform) == false ){ - getFoldersRecursively(dir.getPath(i), folderNames, platform); + if (!fs::exists(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console + if (!fs::is_directory(path)) return; + if (path.extension() != ".framework") { + for (const auto & entry : fs::directory_iterator(path)) { + auto f = entry.path(); + if (f.filename().c_str()[0] == '.') continue; // avoid hidden files .DS_Store .vscode .git etc + if (fs::is_directory(f) && isFolderNotCurrentPlatform(f.string(), platform) == false ) { + getFoldersRecursively(f, folderNames, platform); } } - // FIXME: convert folderNames to path - folderNames.push_back(path.string()); + folderNames.emplace_back(path.string()); } } - -void getFrameworksRecursively(const fs::path & path, std::vector < std::string > & frameworks, std::string platform){ - - - ofDirectory dir; - dir.listDir(path); - - for (int i = 0; i < dir.size(); i++){ - - ofFile temp(dir.getFile(i)); - - if (temp.isDirectory()){ - //getLibsRecursively(dir.getPath(i), folderNames); - - // on osx, framework is a directory, let's not parse it.... - std::string ext = ""; - std::string first = ""; - splitFromLast(dir.getPath(i), ".", first, ext); - if (ext != "framework") - getFrameworksRecursively(dir.getPath(i), frameworks, platform); - else - frameworks.push_back(dir.getPath(i)); +void getFrameworksRecursively(const fs::path & path, std::vector < std::string > & frameworks, std::string platform) { + if (!fs::exists(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console + if (!fs::is_directory(path)) return; + for (const auto & entry : fs::directory_iterator(path)) { + auto f = entry.path(); + if (f.filename().c_str()[0] == '.') continue; // avoid hidden files .DS_Store .vscode .git etc + + if (fs::is_directory(f)) { + if (f.extension() == ".framework") { + frameworks.emplace_back(f.string()); + } else { + if (f.filename() == fs::path("mediaAssets")) continue; + if (f.extension() == ".xcodeproj") continue; + if( f.string().rfind("example", 0) == 0) continue; + // if (f.filename() == fs::path(".git")) continue; + + getFrameworksRecursively(f, frameworks, platform); + } } - } } void getPropsRecursively(const fs::path & path, std::vector < std::string > & props, const std::string & platform) { + if (!fs::exists(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console + if (!fs::is_directory(path)) return; + for (const auto & entry : fs::directory_iterator(path)) { + auto f = entry.path(); + if (f.filename().c_str()[0] == '.') continue; // avoid hidden files .DS_Store .vscode .git etc - if(!ofDirectory::doesDirectoryExist(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console - ofDirectory dir; - dir.listDir(path); + if (fs::is_directory(f)) { + if (f.filename() == fs::path("mediaAssets")) continue; + if (f.extension() == ".xcodeproj") continue; + if( f.string().rfind("example", 0) == 0) continue; +// if (f.filename() == fs::path(".git")) continue; - for (auto & temp : dir) { - if (temp.isDirectory()) { - //skip example directories - this is needed as we are search all folders in the addons root path - if( temp.getFileName().rfind("example", 0) == 0) continue; - getPropsRecursively(temp.path(), props, platform); - } - else { - std::string ext = ""; - std::string first = ""; - splitFromLast(temp.path(), ".", first, ext); - if (ext == "props") { - props.push_back(temp.path()); + getPropsRecursively(f, props, platform); + } else { + if (f.extension() == ".props") { +// cout << f << endl; + props.emplace_back(f); } } - } } void getDllsRecursively(const fs::path & path, std::vector < std::string > & dlls, std::string platform) { - ofDirectory dir; - dir.listDir(path); + if (!fs::exists(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console + if (!fs::is_directory(path)) return; + if (path.filename().c_str()[0] == '.') return; // avoid hidden files .DS_Store .vscode .git etc + for (const auto & entry : fs::directory_iterator(path)) { + auto f = entry.path(); + if (fs::is_directory(f)) { + getDllsRecursively(f, dlls, platform); + } else { + if (f.extension() == ".dll") { + cout << "---->> getDLLs " << f << endl;; - for (auto & temp : dir) { - if (temp.isDirectory()) { - getDllsRecursively(temp.path(), dlls, platform); - } - else { - std::string ext = ""; - std::string first = ""; - splitFromLast(temp.path(), ".", first, ext); - if (ext == "dll") { - dlls.push_back(temp.path()); + dlls.emplace_back(f); } } - } } -// -// -// if (ofIsStringInString(f.filename().string(),".framework")) continue; // ignore frameworks -// -// if (fs::is_directory(f)) { -// if (f.filename() != fs::path(".git")) { // ignore git dir -// getFilesRecursively(f, fileNames); -// } -// } -// else { -// fileNames.emplace_back(f); -// } -//} - -using std::cout; -using std::endl; void getLibsRecursively(const fs::path & path, std::vector < std::string > & libFiles, std::vector < LibraryBinary > & libLibs, std::string platform, std::string arch, std::string target){ -// cout << "getLibsRecursively " << path << endl; -// ofDirectory dir; -// dir.listDir(path); - - - -// for (int i = 0; i < dir.size(); i++){ + if (!fs::exists(path)) return; //check for dir existing before listing to prevent lots of "source directory does not exist" errors printed on console + if (!fs::is_directory(path)) return; for (const auto & entry : fs::directory_iterator(path)) { auto f = entry.path(); - std::vector splittedPath = ofSplitString(f, fs::path("/").make_preferred().string()); // ofFile temp(dir.getFile(i)); @@ -375,6 +333,7 @@ void getLibsRecursively(const fs::path & path, std::vector < std::string > & lib if (ext == "a" || ext == "lib" || ext == "dylib" || ext == "so" || (ext == "dll" && platform != "vs")){ if (platformFound){ +// libLibs.emplace_back( f, arch, target ); libLibs.push_back({ f, arch, target }); //TODO: THEO hack @@ -386,13 +345,14 @@ void getLibsRecursively(const fs::path & path, std::vector < std::string > & lib if( currentPath.find("ofxOpenCv") == std::string::npos ){ ofStringReplace(currentPath, "ios", "osx"); if( fs::exists(currentPath) ){ +// libLibs.emplace_back( currentPath, arch, target ); libLibs.push_back({ currentPath, arch, target }); } } } } } else if (ext == "h" || ext == "hpp" || ext == "c" || ext == "cpp" || ext == "cc" || ext == "cxx" || ext == "m" || ext == "mm"){ - libFiles.push_back(f); + libFiles.emplace_back(f); } } } @@ -413,17 +373,13 @@ std::string unsplitString (std::vector < std::string > strings, std::string deli } -static std::string OFRoot = "../../.."; +static fs::path OFRoot { "../../.." }; -std::string getOFRoot(){ - return ofFilePath::removeTrailingSlash(OFRoot); +fs::path getOFRoot(){ + return OFRoot; } -std::string getAddonsRoot(){ - return ofFilePath::join(getOFRoot(), "addons"); -} - -void setOFRoot(std::string path){ +void setOFRoot(const fs::path & path){ OFRoot = path; } @@ -509,3 +465,13 @@ unique_ptr getTargetProject(ofTargetPlatform targ) { return unique_ptr(); } } + +std::string colorText(const std::string & s, int color) { + std::string c = std::to_string(color); + return "\033[1;"+c+"m" + s + "\033[0m"; +} + +void alert(std::string msg, int color) { + std::cout << colorText(msg, color) << std::endl; +} + diff --git a/commandLine/src/utils/Utils.h b/commandLine/src/utils/Utils.h index a0604829..f971757d 100644 --- a/commandLine/src/utils/Utils.h +++ b/commandLine/src/utils/Utils.h @@ -21,9 +21,8 @@ std::string generateUUID(std::string input); -std::string getOFRoot(); -std::string getAddonsRoot(); -void setOFRoot(std::string path); +fs::path getOFRoot(); +void setOFRoot(const fs::path & path); void findandreplace( std::string& tInput, std::string tFind, std::string tReplace ); void findandreplaceInTexfile (const of::filesystem::path & fileName, std::string tFind, std::string tReplace ); @@ -47,6 +46,7 @@ void splitFromFirst(std::string toSplit, std::string deliminator, std::string & void fixSlashOrder(std::string & toFix); std::string unsplitString (std::vector < std::string > strings, std::string deliminator ); +// FIXME: FS std::string getOFRelPath(const std::string & from); of::filesystem::path getOFRelPathFS(const of::filesystem::path & from); @@ -70,4 +70,9 @@ inline bool isInVector(T item, std::vector & vec){ return bIsInVector; } +std::string colorText(const std::string & s, int color = 32); +void alert(std::string msg, int color=32); + + + #endif /* UTILS_H_ */