XCScheme::XCScheme(const String& absSchemePath, const PBXProject* owner) : m_absPath(absSchemePath), m_parentProject(owner) { // Record the scheme name m_name = sb_fname(sb_basename(m_absPath)); #if defined(_MSC_VER) // Disambiguate scheme names from different users String userDir = sb_basename(sb_dirname(sb_dirname(m_absPath))); if (strEndsWith(userDir, ".xcuserdatad")) m_name = m_name + " (" + sb_fname(userDir) + ")"; #endif }
static VCProjectItem* addFileToVSInternal(const PBXFile* file, VCProject& proj, const BuildSettings& bs, bool isVariant, const VCItemHint* itemHint) { // Add all children of any PBXVariantGroup const PBXVariantGroup* variantGroup = dynamic_cast<const PBXVariantGroup*>(file); if (variantGroup) { const ConstFileList& children = variantGroup->getChildren(); for (auto child : children) { addFileToVSInternal(child, proj, bs, true, itemHint); } return NULL; } // Get the real and virtual paths for the file in Xcode String realPath = file->getFullPath(); String virtualPath = file->getVirtualPath(); // Fix up virtual path for variant files // Resources/MainStoryboard.storyboard/en => Resources/en/MainStoryboard.storyboard if (isVariant) { String variantLang = sb_basename(virtualPath); String filePath = sb_dirname(virtualPath); String fixedDir = joinPaths(sb_dirname(filePath), variantLang); virtualPath = joinPaths(fixedDir, sb_basename(filePath)); } // Compute the VS ItemType for the file String fileType = file->getFileType(); String vsType = getVSItemType(fileType); if (vsType == "Unknown") { if (itemHint && !itemHint->defaultType.empty()) { vsType = itemHint->defaultType; } } // Add the item to the project, taking into account path overrides VCProjectItem* item = NULL; if (itemHint && !itemHint->pathOverride.empty()) { sbAssert(!isVariant, "Unexpected path override for variant file: " + realPath); item = proj.addItem(vsType, itemHint->pathOverride, sb_dirname(virtualPath)); } else { item = addRelativeFilePathToVS(vsType, realPath, sb_dirname(virtualPath), proj, bs); } // Handle Variant files if (isVariant) { String variantDir = sb_basename(sb_dirname(realPath)); item->setDefinition("VariantDir", variantDir); } return item; }
String PBXFile::getFileType(const String& fileName) { // Get the lowercase extension std::string ext = sb_fextension(sb_basename(fileName)); for (int i = 0; i < ext.length(); i++) ext[i] = tolower(ext[i]); if (ext == "c") return "sourcecode.c.c"; else if (ext == "cpp" || ext == "cc" || ext == "cxx") return "sourcecode.cpp.cpp"; else if (ext == "m") return "sourcecode.c.objc"; else if (ext == "mm" || ext == "mxx") return "sourcecode.cpp.objcpp"; else if (ext == "s") return "sourcecode.asm"; else if (ext == "h" || ext == "pch") return "sourcecode.c.h"; else if (ext == "hpp" || ext == "hh") return "sourcecode.cpp.h"; else if (ext == "d") return "sourcecode.dtrace"; else if (ext == "png") return "image.png"; else if (ext == "tiff") return "image.tiff"; else if (ext == "jpg" || ext == "jpeg") return "image.jpeg"; else if (ext == "mp3") return "audio.mp3"; else if (ext == "xib") return "file.xib"; else if (ext == "nib" || ext == "nib~") return "file.xib"; else if (ext == "plist" || ext == "dict") return "text.plist.xml"; else if (ext == "strings") return "text.plist.strings"; else if (ext == "storyboard") return "file.storyboard"; else if (ext == "storyboardc") return "wrapper.storyboardc"; else if (ext == "xcdatamodel") return "wrapper.xcdatamodel"; else if (ext == "xcdatamodeld") return "wrapper.xcdatamodeld"; else if (ext == "o" || ext == "obj") return "compiled.mach-o.objfile"; else if (ext == "bundle") return "wrapper.plug-in"; else if (ext == "xcassets") return "folder.assetcatalog"; else if (ext == ".app") return "wrapper.application"; else if (ext == ".a") return "archive.ar"; else return "text"; }
void printUsage(const char *execName, bool full, int exitCode) { std::cout << "Usage: "; std::cout << "\t" << sb_basename(execName) << " "; std::cout << "[-project projectname] [-target targetname ...] [-configuration configurationname] "; std::cout << "[-interactive] [setting=value ...]"; std::cout << std::endl; std::cout << "\t" << sb_basename(execName) << " "; std::cout << "[-project projectname] -scheme schemename [-configuration configurationname] "; std::cout << "[-interactive] [setting=value ...]"; std::cout << std::endl; std::cout << "\t" << sb_basename(execName) << " "; std::cout << "-workspace workspacename -scheme schemename [-configuration configurationname] "; std::cout << "[-interactive] [setting=value ...]"; std::cout << std::endl; std::cout << "\t" << sb_basename(execName) << " "; std::cout << "-list [-project projectname | -workspace workspacename]" << std::endl; // Don't print option descriptions if brief usage was requested if (!full) goto done; std::cout << std::endl; std::cout << "Program Options" << std::endl; std::cout << " -usage" << "\t\t print brief usage message" << std::endl; std::cout << " -help" << "\t\t print full usage message" << std::endl; std::cout << " -interactive" << "\t enable interactive mode" << std::endl; std::cout << " -loglevel LEVEL" << "\t debug | info | warning | error" << std::endl; std::cout << " -list" << "\t\t list the targets and configurations in the project" << std::endl; std::cout << " -sdk SDKROOT" << "\t specify path to WinObjC SDK root (by default calculated from binary's location)" << std::endl; std::cout << " -relativepath" << "\t write a relative WinObjC SDK path to project files" << std::endl; std::cout << " -project PATH" << "\t specify project to process" << std::endl; std::cout << " -workspace PATH" << "\t specify workspace to process" << std::endl; std::cout << " -target NAME" << "\t specify target to process" << std::endl; std::cout << " -alltargets" << "\t\t process all targets" << std::endl; std::cout << " -scheme NAME" << "\t specify scheme to process" << std::endl; std::cout << " -allschemes" << "\t\t process all schemes" << std::endl; std::cout << " -configuration NAME" << "\t specify configuration to use" << std::endl; std::cout << " -xcconfig FILE" << "\t apply build settings defined in FILE as overrides" << std::endl; std::cout << " -version" << "\t\t print the tool version" << std::endl; done: exit(exitCode); }
// TODO: use function pointers const PBXTarget* PBXProject::getTargetWithProductName(const String& productName) const { for (unsigned i = 0; i < m_targetPtrs.size(); i++) { String productFile = sb_basename(m_targetPtrs[i]->getProductFileName()); if (productFile == productName) return m_targetPtrs[i]; } return NULL; }
void SBResourcesBuildPhase::writeVCProjectFiles(VCProject& proj) const { TargetProductType productType = m_parentTarget.getProductType(); if (productType != TargetApplication && productType != TargetBundle) { return; } // Process build files const BuildSettings& projBS = m_parentTarget.getProject().getBuildSettings(); const BuildFileList& buildFiles = m_phase->getBuildFileList(); sbAssert(buildFiles.size() == m_buildFileTargets.size()); for (size_t i = 0; i < buildFiles.size(); i++) { // Construct a path for Bundle build products, relative to the SolutionDir, // instead of using the Xcode path String pathOverride; if (m_buildFileTargets[i]) { String productFileName = sb_basename(buildFiles[i]->getFile()->getFullPath()); String productFileType = buildFiles[i]->getFile()->getFileType(); if (productFileType == "wrapper.cfbundle") { pathOverride = "$(SolutionDir)$(Configuration)\\" + productFileName; } else { SBLog::warning() << "Unexpected build product in ResourceBuildPhase: " << productFileName << std::endl; } } VCItemHint itemHint = { "SBResourceCopy" , pathOverride }; addBuildFileToVS(buildFiles[i], proj, projBS, &itemHint); } // Process all Info.plist files std::map<std::string, VCProjectItem*> infoPlistMap; for (auto bs : m_parentTarget.getBuildSettings()) { VCProjectConfiguration* config = proj.addConfiguration(bs.first); // Exclude all plist from building, by default config->setItemDefinition("SBInfoPlistCopy", "ExcludedFromBuild", "true"); // Get absolute path to plist String plistPath = bs.second->getValue("INFOPLIST_FILE"); plistPath = m_parentTarget.makeAbsolutePath(plistPath); // Add plist file to project (only once) if (infoPlistMap.find(plistPath) == infoPlistMap.end()) { infoPlistMap[plistPath] = addRelativeFilePathToVS("SBInfoPlistCopy", plistPath, "", proj, *bs.second); } // Un-exclude building plist for configuration String condition = "'$(Configuration)'=='" + bs.first + "'"; infoPlistMap[plistPath]->setDefinition("ExcludedFromBuild", "false", condition); // Specify which variables files to use String varsFile = m_parentTarget.getName() + "-" + bs.first + "-xcvars.txt"; infoPlistMap[plistPath]->setDefinition("VariableFile", varsFile, condition); } }
void SBTarget::validateSDK() { // Check if this is an iphoneos target // Could take the form of "iphone" or a full path to the SDK for (auto bs : m_buildSettings) { const VariableCollectionHierarchy& vch = bs.second->getHierarchy(); String sdkRoot = vch.getValue("SDKROOT", vch.size() - 2); sdkRoot = strToLower(sb_basename(sdkRoot)); if (!strBeginsWith(sdkRoot, "iphoneos")) { SBLog::warning() << "The \"" << bs.first << "\" configuration of the \"" << getName() << "\" target does not target an iOS SDK." << std::endl; } } }
void PBXFile::initFromPlist(const String& id, const Plist::dictionary_type& plist, const PBXDocument* pbxDoc) { // Call super init PBXObject::initFromPlist(id, plist, pbxDoc); // Get path getStringForKey(plist, "path", m_path, VALUE_OPTIONAL, m_parseER); // Get name getStringForKey(plist, "name", m_name, VALUE_OPTIONAL, m_parseER); if (m_name.empty()) m_name = sb_basename(m_path); // Get sourceTree getStringForKey(plist, "sourceTree", m_sourceTree, VALUE_REQUIRED, m_parseER); }
void PBXNativeTarget::getBuildSettings(VariableCollection& settings) const { PBXTarget::getBuildSettings(settings); String productFileType = getProductFileType(); String productPath = getProductFileName(); String productNameFull = sb_basename(productPath); String productName = sb_fname(productNameFull); if (m_productType == "com.apple.product-type.library.static") { if (productFileType != "archive.ar") SBLog::warning() << "Unexpected product file type \"" << productFileType << "\" for \"" << getName() << "\" static lib target." << std::endl; settings.insert("EXECUTABLE_NAME", productNameFull); settings.insert("EXECUTABLE_PATH", productNameFull); } else if (m_productType == "com.apple.product-type.framework") { if (productFileType != "wrapper.framework") { SBLog::warning() << "Unexpected product file type \"" << productFileType << "\" for \"" << getName() << "\" framework target." << std::endl; } settings.insert("EXECUTABLE_NAME", productName); settings.insert("EXECUTABLE_PATH", joinPaths(productNameFull, productName)); } else if (m_productType == "com.apple.product-type.application") { // Fix up product name, when necessary if (productFileType == "compiled.mach-o.executable") productNameFull = productName + ".app"; else if (productFileType != "wrapper.application") SBLog::warning() << "Unexpected product file type \"" << productFileType << "\" for \"" << getName() << "\" app target." << std::endl; settings.insert("EXECUTABLE_NAME", productName); settings.insert("EXECUTABLE_FOLDER_PATH", productNameFull); } else if (m_productType == "com.apple.product-type.bundle") { if (productFileType != "wrapper.cfbundle") { SBLog::warning() << "Unexpected product file type \"" << productFileType << "\" for \"" << getName() << "\" bundle target." << std::endl; } settings.insert("EXECUTABLE_NAME", productName); settings.insert("EXECUTABLE_FOLDER_PATH", productNameFull); } settings.insert("PRODUCT_NAME", productName); settings.insert("FULL_PRODUCT_NAME", productNameFull); settings.insert("PRODUCT_TYPE", m_productType); }
bool VSTemplateProject::initFromXML(const pugi::xml_node& pNode) { // Create a ProjectItem from the project description ProjectItem* projectDesc = new ProjectItem; projectDesc->inFile = pNode.attribute("File").value(); projectDesc->outFile = pNode.attribute("TargetFileName").value(); projectDesc->replaceParams = pNode.attribute("ReplaceParameters").as_bool(); m_items.push_back(projectDesc); tokenize(pNode.attribute("Platforms").value(), m_platforms, ";"); m_shared = pNode.attribute("Shared").as_bool(); m_deployable = pNode.attribute("Deployable").as_bool(); m_outputDir = pNode.attribute("OutputDirectory").value(); if (m_outputDir.empty()) { m_outputDir = "$projectname$"; } // Extract project items for (pugi::xml_node child = pNode.first_child(); child; child = child.next_sibling()) { if (child.name() == std::string("ProjectItem")) { bool replaceParams = child.attribute("ReplaceParameters").as_bool(); std::string inFile = child.child_value(); std::string outFile = child.attribute("TargetFileName").value(); if (outFile.empty()) { outFile = sb_basename(inFile); } ProjectItem* item = new ProjectItem(inFile, outFile, replaceParams); m_items.push_back(item); } } // Validate if (m_platforms.empty() && !m_shared) { SBLog::warning() << "Ignoring " << projectDesc->inFile << " project because it specifies no platforms." << std::endl; return false; } return true; }
void SBFrameworksBuildPhase::writeVCProjectFiles(VCProject& proj) const { // We don't support linking with frameworks when building bundles TargetProductType productType = m_parentTarget.getProductType(); if (productType == TargetBundle) { if (!m_phase->getBuildFileList().empty()) { SBLog::warning() << "Ignoring all frameworkss in \"" << m_parentTarget.getName() << "\" bundle target." << std::endl; } return; } String linkTarget; if (productType == TargetApplication) linkTarget = "Link"; else if (productType == TargetStaticLib) linkTarget = "Lib"; // Get paths to all the build files (frameworks) StringVec buildFilePaths; if (m_phase) { const BuildFileList& buildFiles = m_phase->getBuildFileList(); sbAssert(buildFiles.size() == m_buildFileTargets.size()); for (size_t i = 0; i < buildFiles.size(); i++) { const PBXFile* file = buildFiles[i]->getFile(); // Ignore any frameworks build from source (they will be added as project references) if (file && !m_buildFileTargets[i]) buildFilePaths.push_back(file->getFullPath()); } } for (auto bs : m_parentTarget.getBuildSettings()) { VCProjectConfiguration* config = proj.addConfiguration(bs.first); // Extrace libs/frameworks from OTHER_LDFLAGS StringVec buildFilePaths(buildFilePaths); processLDFlags(bs.second->getValue("OTHER_LDFLAGS"), buildFilePaths); // Construct a list of libraries to link against StringSet linkedLibs; linkedLibs.insert("%(AdditionalDependencies)"); for (auto filePath : buildFilePaths) { if (productType == TargetStaticLib && !strEndsWith(filePath, ".a")) continue; String winLibName = sb_fname(sb_basename(filePath)) + ".lib"; // If the library is blocked then add the replacement library to our additional dependencies auto it = s_blockedLibraries.find(winLibName); while (it != s_blockedLibraries.end()) { // get the replacement library. winLibName = it->second; // follow any transitive replacement. it = s_blockedLibraries.find(winLibName); } if (!winLibName.empty()) { linkedLibs.insert(winLibName); } } // AdditionalDependencies String additionalDeps = joinStrings(linkedLibs, ";"); if (!additionalDeps.empty()) { config->setItemDefinition(linkTarget, "AdditionalDependencies", additionalDeps); } } }
SBTarget* SBTarget::getPossibleTarget(const PBXBuildFile* buildFile) { static const char* const _productWildcards[] = {"lib*.a", "*.app", "*.framework"}; static StringVec productWildcards(_productWildcards, _productWildcards + sizeof(_productWildcards) / sizeof(char*)); sbAssert(buildFile); const PBXFile* file = buildFile->getFile(); String filePath, fileName; if (file) { filePath = file->getFullPath(); fileName = sb_basename(filePath); } SBTarget* depTarget = NULL; const PBXReferenceProxy* proxyFile = NULL; // We are interested in any potential Xcode build products if (!matchWildcardList(fileName, productWildcards)) { // Do nothing } else if ((proxyFile = dynamic_cast<const PBXReferenceProxy*>(file))) { // Construct base error string, to hopefully never be used String errStr = "Failed to process PBXBuildFile (" + buildFile->getId() + ") for " + getName() + " target. "; // Get remote proxy container const PBXContainerItemProxy* container = proxyFile->getContainer(); // Ignore build file, if necessary if (!container) { SBLog::warning() << errStr << "Unable to get the PBXContainerItemProxy."; return NULL; } // Get remote project and target identifier from the proxy const String& remoteId = container->getRemoteId(); String projectPath = container->getPortalPath(); // Expand the project path const BuildSettings& projBS = m_parentProject.getBuildSettings(); String absProjectPath = projBS.expand(projectPath, PathValue); // Try to open the remote project SBProject* remoteProject = SBWorkspace::get()->openProject(absProjectPath); // Try to queue up the target with the given product reference if (remoteProject) { depTarget = remoteProject->queueTargetWithProductReference(remoteId); if (!depTarget) SBLog::warning() << errStr << "Unable to create proxy target " << remoteId << " from the \"" << remoteProject->getName() << "\" project." << std::endl; } else { SBLog::warning() << errStr << "Unable to open referenced project path: " << projectPath << std::endl; } } else { // Look for target in current project first depTarget = m_parentProject.queueTargetWithProductName(fileName); // Look for target in workspace, if it hasn't been found already if (!depTarget) depTarget = SBWorkspace::get()->queueTargetWithProductName(fileName); } // Add the target to the dependency set addDependency(depTarget); // Indicate whether this was a dependency or not return depTarget; }
void printVersion(const char *execName) { static String binaryVersion = "1.0"; std::cout << sb_basename(execName) << " " << binaryVersion << std::endl; exit(EXIT_SUCCESS); }