def add_configuration_linker_properties(settings, project, context, architectures, configurations): for architecture in architectures: for config in configurations: #logging.info("TODO: update platform in configuration linker properties") context.update(architecture=architecture, configuration=config, platform="windows") layout = context.get_layout(architecture, config) linker_props = { "LinkIncremental" : "true" } # warning MSB8004: <Output/Intermediate> Directory does not end with a trailing slash. # these are relative to the vcxproj; so we need to convert these from the product root. paths = [os.path.normpath(context.product_root), os.path.normpath(context.object_root)] paths = make_paths_relative_to_project(context, paths) linker_props["OutDir"] = paths[0] + os.path.sep linker_props["IntDir"] = paths[1] + os.path.sep arch = MSBuildArchitectureMap(architecture) config_arch_combo = "%s|%s" % (config, str(arch)) property_group = PropertyGroup(settings, { "Condition" : "'$(Configuration)|$(Platform)'=='%s'" % config_arch_combo }, text_nodes=linker_props) project.add(property_group)
def generate(self, **kwargs): executor = kwargs.get("executor", None) buildfile = kwargs.get("buildfile", None) filemanager = kwargs.get("filemanager", None) target_platform = kwargs.get("target_platform", None) context_list = kwargs.get("context_list", None) for context in context_list: architectures, configurations = buildfile.get_layout_params(context.product) # get the full project path for this full_project_path = os.path.join(context.abs_project_root, ("%s.vcxproj" % context.product.name)) # create the project and open a FileWriter object. handle = filemanager.create(full_project_path) writer = FileWriter(handle) document = Document() settings = VSSettings( document=document, version=self.requested_version, settings=self.driver_schema ) # establish the root project node properties = { "DefaultTargets": settings.get("DefaultTargets"), "ToolsVersion": settings.get("ToolsVersion"), # this resource was moved by microsoft; but is required for VS to load the project. "xmlns": "http://schemas.microsoft.com/developer/msbuild/2003" } project = Project(settings, properties) document.appendChild(project.node) # # setup the project configurations item group # item_group = ItemGroup(settings, {"Label" : "ProjectConfigurations"}) project.node.appendChild(item_group.node) # add_project_configurations(settings, item_group, architectures, configurations) # # Globals property group # project_guid = "{%s}" % str(uuid.uuid4()).upper() # the ProjectGuid is different between VS2010 and VS2013. (probably other versions as well, need to figure these out eventually.) global_props = OrderedDict([ ("ProjectGuid", project_guid), #("Keyword", "Win32Proj"), ("RootNamespace", context.product.name) ]) # Starting with Visual Studio 2017, there's a new key, WindowsTargetPlatformVersion if self.latest_kit_version is not None: # prefer the latest Windows Kit version, if we found one. global_props['WindowsTargetPlatformVersion'] = self.latest_kit_version elif settings.get('WindowsTargetPlatformVersion') is not None: # As a fallback, use the value we were specifying in the config. global_props['WindowsTargetPlatformVersion'] = '10.0.17134.0' global_property_group = PropertyGroup(settings, {"Label" : "Globals"}, text_nodes=global_props) project.node.appendChild(global_property_group.node) # # Import default project properties # default_properties_import = Import(settings, OrderedDict([ ("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props") ])) project.add(default_properties_import) # # Configuration property groups # add_project_groups(settings, project, context, architectures, configurations) # # Import Cpp props # cpp_props_import = Import(settings, { "Project": "$(VCTargetsPath)\\Microsoft.Cpp.props" }) project.add(cpp_props_import) # # Extension Settings import group # extension_settings = ImportGroup(settings, {"Label" : "ExtensionSettings"}) project.add(extension_settings) # # Property Sheets import group for each configuration # add_configuration_property_sheets(settings, project, context.product, architectures, configurations) # # UserMacros property group # user_macros_group = PropertyGroup(settings, OrderedDict([ ("Label", "UserMacros") ])) project.add(user_macros_group) # # PropertyGroups for each configuration # add_configuration_linker_properties(settings, project, context, architectures, configurations) # # ItemDefinitionGroups for each platform # add_configuration_definition_groups(settings, self.driver_schema, project, context, architectures, configurations) # # ItemGroup for files # # <ItemGroup> # <ClCompile Include="..\src\common.c" /> # ... # ... # <ClInclude Include="..\include\common.h" /> # this doesn't work as-is. It adds duplicates to the list, unfortunately. # source_group = ItemGroup(settings, {}) # for architecture in architectures: # for configuration in configurations: # params = context.get_layout(architecture=architecture, configuration=configuration) # sources = make_paths_relative_to_project(context, params.sources) # for path in sources: # #logging.info("source: %s" % path) # # make path relative to project # source_group.add(ClCompile(settings, {"Include": path})) # project.add(source_group) # break source_group = ItemGroup(settings, {}) include_group = ItemGroup(settings, {}) params = context.get_layout(architecture=architectures[0], configuration=configurations[0]) params.sources = make_paths_relative_to_project(context, params.sources) self.add_compiled_sources(settings, params.sources, source_group, include_group) project.add(source_group) project.add(include_group) """ source_group = ItemGroup(settings, {}) sources = [] for path in sources: compile_source = ClCompile(settings, {"Include": path}) source_group.add(compile_source) project.add(source_group) includes_group = ItemGroup(settings, {}) includes = [] for path in includes: include = ClInclude(settings, {"Include": path}) includes_group.add(include) project.add(includes_group) """ # # Project targets import # target_import = Import(settings, {"Project": "$(VCTargetsPath)\\Microsoft.cpp.targets"}) project.add(target_import) # # Import Group for extension targets # extension_targets_import = ImportGroup(settings, {"Label" : "ExtensionTargets"}) project.add(extension_targets_import) writer.get_file().write(settings.document.toprettyxml(indent=INDENT, encoding="utf-8")) writer.close() # # write project filters filter_list = self.collect_filters(params.sources) if filter_list: full_filters_path = os.path.join(context.abs_project_root, ("%s.vcxproj.filters" % context.product.name)) filter_file_handle = filemanager.create(full_filters_path) filter_writer = FileWriter(filter_file_handle) document = Document() settings = VSSettings( document=document, version=self.requested_version, settings=self.driver_schema ) properties = { "ToolsVersion": "4.0", # this resource was moved by microsoft; but is required for VS to load the project. "xmlns": "http://schemas.microsoft.com/developer/msbuild/2003" } project = Project(settings, properties) document.appendChild(project.node) source_group = ItemGroup(settings, {}) include_group = ItemGroup(settings, {}) self.add_compiled_sources(settings, params.sources, source_group, include_group, filters=True) project.add(source_group) project.add(include_group) filter_group = ItemGroup(settings, {}) # generate uuids for each filter. for filter_name in filter_list: filter_guid = "{%s}" % str(uuid.uuid4()).upper() filter_props = { "Include": filter_name } filter_id = { "UniqueIdentifier": filter_guid } filter_node = Filter(settings, filter_props, filter_id) filter_group.add(filter_node) project.add(filter_group) filter_writer.get_file().write(settings.document.toprettyxml(indent=INDENT, encoding="utf-8")) filter_writer.close() self.generate_solution(**kwargs)
def add_configuration_definition_groups(settings, schema, project, context, architectures, configurations): main_params = context.params_list[0] # The warnings are specified in the build scripts as a # list of numbers, but python wants the members of a list # given to join to be strings, so we convert those here. disabled_warning_strings = [str(x) for x in main_params.driver.disablespecificwarnings] for architecture in architectures: for config in configurations: #logging.info("TODO: update platform in item definition groups") layout = context.get_layout(architecture, config) arch = MSBuildArchitectureMap(architecture) config_arch_combo = "%s|%s" % (config, str(arch)) #logging.info("TODO: correctly prefix include and libdirs") includes = make_paths_relative_to_project(context, layout.includes) #[prefix_if_relative(x) for x in layout.includes] # inherit includes includes = add_quotes(includes) includes.append("%(AdditionalIncludeDirectories)") # inherit libdirs libdirs = [prefix_if_relative(x, "$(ProjectDir)") for x in layout.libdirs] # temporarily omitting this; it's causing some path problems where # an empty path is added. ugh. #libdirs.append("%(AdditionalLibraryDirectories)") libdirs = add_quotes(libdirs) libdirs.append("%(AdditionalLibraryDirectories)") links = layout.links # setup defines is_debug = "debug" in config.lower() defines = list() if context.product.output == ProductType.Commandline: defines.append("_CONSOLE") if is_debug: defines.append("_DEBUG") [defines.append(item) for item in layout.defines] defines.append("%(PreprocessorDefinitions)") runtime_library_map = { "dynamic": "MultiThreadedDLL", "dynamic_debug": "MultiThreadedDebugDLL", "static": "MultiThreaded", "static_debug": "MultiThreadedDebug" } # setup runtime library if main_params.driver.runtimelibrary == "dynamic": if is_debug: runtime_library = "dynamic_debug" else: runtime_library = "dynamic" elif main_params.driver.runtimelibrary == "static": if is_debug: runtime_library = "static_debug" else: runtime_library = "static" # The disabled warnings are specified as a list of numbers # delimited by semi-colons. See the note above by # disabled_warning_strings. disabled_warnings = ";".join(disabled_warning_strings) compile_opts = OrderedDict([ ("PrecompiledHeader", ""), ("WarningLevel", main_params.driver.warninglevel), ("Optimization", "Disabled"), ("PreprocessorDefinitions", ";".join(defines)), ("AdditionalIncludeDirectories", ";".join(includes)), ("RuntimeLibrary", runtime_library_map[runtime_library]), ("DisableSpecificWarnings", disabled_warnings) ]) subsystem_types = { ProductType.Commandline : "Console", ProductType.Application : "Windows" } subsystem = "Console" if context.product.output in subsystem_types: subsystem = subsystem_types[context.product.output] generate_debug_info = getattr(layout.driver, "generate_debug_info", None) if not generate_debug_info: generate_debug_info = "no" debug_info = schema["generate_debug_info"][generate_debug_info] link_opts = OrderedDict([ ("SubSystem", subsystem), ("GenerateDebugInformation", debug_info), ("AdditionalLibraryDirectories", ";".join(libdirs)), ("AdditionalDependencies", ";".join(links)) ]) item_definitions = ItemDefinitionGroup(settings, OrderedDict([ ("Condition", "'$(Configuration)|$(Platform)'=='%s'" % config_arch_combo) ])) clcompile = ClCompile(settings, None, text_nodes=compile_opts) item_definitions.add(clcompile) link = Link(settings, None, text_nodes=link_opts) item_definitions.add(link) pre_build_opts = OrderedDict([ ("Command", "\r\n".join(layout.prebuild_commands)), ("Message", "Running Pre-Build Commands...") ]) pre_build_event = PreBuildEvent(settings, None, text_nodes=pre_build_opts) item_definitions.add(pre_build_event) pre_link_opts = OrderedDict([ ("Message", "Running Pre-Link Commands...") ]) pre_link_event = PreLinkEvent(settings, None, text_nodes=pre_link_opts) item_definitions.add(pre_link_event) post_build_opts = OrderedDict([ ("Message", "Running Post-Build Commands...") ]) post_build_event = PostBuildEvent(settings, None, text_nodes=post_build_opts) item_definitions.add(post_build_event) project.add(item_definitions)
def generate(self, **kwargs): executor = kwargs.get("executor", None) buildfile = kwargs.get("buildfile", None) filemanager = kwargs.get("filemanager", None) target_platform = kwargs.get("target_platform", None) context_list = kwargs.get("context_list", None) # Key value pair relating the product name to target ref. # This is used when generating schemes so they can setup dependencies. target_ref_dict = {} for context in context_list: product = context.product product_name = product.name output = product.output # Certain attributes must be specified per project (instead of for each configuration/architecture). # This is a limitation of how Xcode handles handles. We can work around this by using # the first params instance in the list. # main_params = context.params_list[0] architectures, configurations = buildfile.get_layout_params(product) sources = make_paths_relative_to_project(context, main_params.sources) resources = make_paths_relative_to_project(context, main_params.resources) project_name = self.get_project_name(product) project_path = os.path.join(context.abs_project_root, project_name) pbxproj_path = os.path.join(project_path, "project.pbxproj") # open a file handle = filemanager.create(pbxproj_path) # instance a new FileWriter and open the path writer = FileWriter(handle) scheme_path = os.path.join(project_path, "xcshareddata", "xcschemes") # ensure this exists filemanager.makedirs(scheme_path) # setup instances for project-wide settings ms = MainSection() prebuildReference = PBXReference(comment="Prebuild") postbuildReference = PBXReference(comment="Postbuild") resourcesReference = PBXReference(comment="Resources") sourcesReference = PBXReference(comment="Sources") frameworksReference = PBXReference(comment="Frameworks") buildPhasesList = [] # compile a list of frameworks. These also need to be in the BuildFile and FileReference sections. frameworks = [] # ... local_ldflags = [] # a list of dynamic libraries libraries = [] prebuild_commands = main_params.prebuild_commands postbuild_commands = main_params.postbuild_commands frameworks = main_params.driver.frameworks libraries = main_params.driver.libraries local_ldflags = main_params.driver.linkflags # setup main section ms.children.append(IntValue("archiveVersion", 1)) ms.children.append(ArrayKeyValue("classes", [])) ms.children.append(IntValue("objectVersion", 46)) # setup frameworks build phase frameworks_build_phase = PBXFrameworksBuildPhaseSection() frameworks_build_phase.frameworks_reference = frameworksReference # all objects objects = [] build_file_section = PBXBuildFileSection() container_item_proxy = PBXContainerItemProxySection() file_reference_section = PBXFileReferenceSection() # file reference dictionary; prevents duplicate file references from being added file_references = {} # files that are compiled in some way source_references = [] # files that are added to the resources build phase resources_references = [] # variant groups that are added to the PBXVariantGroup section variant_groups = [] # framework file references framework_file_references = [] group_map = {} groups = PBXGroupSection() reference_proxies = [] dependent_project_root_items = [] project_group_ref_items = [] target_dependencies = [] for instance in context.ordered_dependencies: xcodeproject_name = "%s.xcodeproj" % instance.name xcp = self.get_project_by_name(instance.name) # create a PBXFileReference for the xcodeproject file_data = self.determine_filereference_class(xcodeproject_name) project_file_ref = PBXFileReference(file_type=file_data.file_class, path=xcodeproject_name, source_tree=RELATIVE_TO_GROUP) file_reference_section.children.append(project_file_ref) file_references[xcodeproject_name] = project_file_ref dependent_project_root_items.append(project_file_ref.reference) # create a container proxy and add it to the section container_proxy = PBXContainerItemProxy( container_portal=project_file_ref.reference, proxy_type=PBXContainerItemProxy.ProxyType.TARGET_REFERENCE, remote_global_id=xcp.target_reference.value, remote_info=instance.name ) target_dependency_proxy = PBXContainerItemProxy( container_portal=project_file_ref.reference, proxy_type=PBXContainerItemProxy.ProxyType.PRODUCT_REFERENCE, remote_global_id=xcp.product_reference.value, remote_info=instance.name ) reference_proxy = PBXReferenceProxy(path=("lib%s.a" % instance.name), remote_ref=target_dependency_proxy.reference) reference_proxies.append(reference_proxy) target_dependency = PBXTargetDependency(name=instance.name, target_proxy=container_proxy.reference) target_dependencies.append(target_dependency) data = { "name": instance.name, "container_portal": project_file_ref.reference, "reference_proxy": reference_proxy } project_group_ref_items.append(data) # this is so they're added in the order that Xcode normally adds them in: proxyType 2 then 1. container_item_proxy.children.append(target_dependency_proxy) container_item_proxy.children.append(container_proxy) # The main project also needs a PBXBuildFile reference pointing to the # dependent library. # This gets referenced in the frameworks build phase. library_buildfile = PBXBuildFile(build_phase=frameworksReference, file_ref=reference_proxy.reference) frameworks_build_phase.files().append(library_buildfile.reference) build_file_section.children.append(library_buildfile) # logging.info([d.name for d in context.ordered_dependencies]) # these groups end up in the project file view in an Xcode project. libraries_group = self.create_group(name="Libraries", children=[], source_tree=RELATIVE_TO_GROUP) frameworks_group = self.create_group(name="Frameworks", children=[], source_tree=RELATIVE_TO_GROUP) group_root = self.create_group(name=product_name, children=[libraries_group.key, frameworks_group.key], source_tree=RELATIVE_TO_GROUP) # process library files (these are to placed into the project's "Libraries" folder and linked with in the Frameworks Build Phase) for library_path in libraries: base_name = os.path.basename(library_path) dir_name = os.path.dirname(library_path) file_data = self.determine_filereference_class(base_name) if base_name not in file_references: file_ref = PBXFileReference(file_type=file_data.file_class, name=base_name, path=library_path, source_tree=RELATIVE_TO_GROUP) file_reference_section.children.append(file_ref) file_references[base_name] = file_ref else: file_ref = file_references[base_name] build_ref = PBXBuildFile(build_phase=frameworksReference, file_ref=file_ref.reference) build_file_section.children.append(build_ref) frameworks_build_phase.files().append(build_ref.reference) libraries_group.children().append(file_ref.reference) # build all file references # This creates PBXBuildFiles, PBXFileReferences, and organizes them in the Project tree within Xcode. # Frameworks, Dynamic Libraries, and Resources are also in this mix. They are typically added to a different build phase. # This should handle relative paths by combining ".." strings appropriately. # sorted_source_list = sorted(sources+frameworks+resources, key=get_sort_key) self.organize_sources(sorted_source_list, context, group_root, group_map, groups, frameworksReference, resourcesReference, sourcesReference, file_references, file_reference_section, build_file_section, source_references, resources_references, frameworks_group, frameworks_build_phase) # On Adding a new group.. # Create a new group (self.create_group) # Populate with children # Add new group to the groups.children # Insert the Frameworks reference somewhere in the application children # If this is a DynamicLibrary or Bundle, a postbuild step is added to update the install_name to reflect # the relative path of the product. if product.output == ProductType.DynamicLibrary or product.output == ProductType.MacOSX_Bundle: context.update(architecture="<VAR>{CURRENT_ARCH}", configuration="<VAR>{CONFIGURATION}") # get the full path to this product full_path = os.path.join(context.product_root, target_platform.get_full_product_path(product)) # then the relative path... output_path = os.path.relpath(full_path, main_params.context.product.project_root) # find the install name for the Root Product install_name_format = target_platform.get_product_install_name(context.product) install_name = install_name_format % target_platform.get_full_product_name(product) install_name_tool = InstallNameTool(command="id", name=install_name, input=output_path) #logging.info(install_name_tool.commandline()) postbuild_commands.append(install_name_tool.commandline()) # retrieve the product data for the output type product_data = self.get_product_data(output) explicit_file_type = product_data["file_type"] extension = product_data["extension"] applicationBundleName = self.get_bundle_name(product) appFileReference = PBXReference(comment=applicationBundleName) frprops = [ IdValue("isa", "PBXFileReference"), IdValue("explicitFileType", explicit_file_type), IntValue("includeInIndex", 0), StringValue("name", applicationBundleName), StringValue("path", applicationBundleName), IdValue("sourceTree", "BUILT_PRODUCTS_DIR") ] fr = ArrayKeyValue(appFileReference, frprops) file_reference_section.children.append(fr) # PBXBuildFile objects.append(build_file_section) # PBXContainerItemProxy objects.append(container_item_proxy) # PBXFileReference objects.append(file_reference_section) # # add frameworks builds phase objects.append(frameworks_build_phase) # root level references group_items = [] for key, value in group_map.iteritems(): if not "/" in key: group_items.append(value[key].key) # setup the root reference: # add top level groups to this. group_root.children().extend(group_items) group_root.children().extend(dependent_project_root_items) # add Product reference products = self.create_group(name="Products", children=[appFileReference], source_tree=RELATIVE_TO_GROUP) groups.children.append(group_root) if libraries_group.children(): groups.children.append(libraries_group) if frameworks_group.children(): groups.children.append(frameworks_group) groups.children.append(products) group_root.children().append(products.key) # loop through container refs and add a product group for them objects.append(groups) nativeTargetConfiguration = PBXReference(comment="Build configuration list for PBXNativeTarget \"%s\"" % product_name) # # The list must maintain the ordering of the references. # 1. Copy resources buildPhasesList.append(resourcesReference) # 2. (if exists) Prebuild commands if prebuild_commands: buildPhasesList.append(prebuildReference) # 3. Sources (compile) buildPhasesList.append(sourcesReference) # 4. Link with Frameworks buildPhasesList.append(frameworksReference) # 5. (if exists) Postbuild commands if postbuild_commands: buildPhasesList.append(postbuildReference) # # Setup the target reference targetRef = PBXReference(comment=product_name) targetRefProps = [ IdValue("isa", "PBXNativeTarget"), IdValue("buildConfigurationList", nativeTargetConfiguration), ListValue("buildPhases", buildPhasesList), ListValue("buildRules", []), ListValue("dependencies", [dep.reference for dep in target_dependencies]), IdValue("name", product_name), # was StringValue, but Xcode changes these to IdValues StringValue("productInstallPath", "$(HOME)/Applications"), IdValue("productName", product_name), # was StringValue, but Xcode changes these to IdValues IdValue("productReference", appFileReference), StringValue("productType", product_data["type"]) ] target = ArrayKeyValue(targetRef, targetRefProps) target_ref_dict[product_name] = targetRef nativetargets = PBXNativeTargetSection() nativetargets.children.append(target) objects.append(nativetargets) xcodeproj = XcodeProject(product=product, product_reference=appFileReference, target_reference=targetRef) self.add_project_by_name(product.name, xcodeproj) projectBuildConfigurationList = PBXReference(comment="Build configuration list for PBXProject \"%s\"" % product_name) project_ref_list = [] for data in project_group_ref_items: #product_group = create_group product_group = self.create_group(name="Products", children=[data["reference_proxy"].reference], source_tree=RELATIVE_TO_GROUP) groups.children.append(product_group) project_ref_items = [ IdValue("ProductGroup", product_group.key), IdValue("ProjectRef", data["container_portal"]) ] # construct root items project_ref_list.append(ArrayValue(None, project_ref_items)) # this later becomes our rootObject. projectRef = PBXReference(comment="Project object") projectProperties = [ IdValue("isa", "PBXProject"), IdValue("attributes", ArrayValue(None, [IdValue("LastUpgradeCheck", LAST_UPGRADE_CHECK)])), # new in Xcode 5 IdValue("buildConfigurationList", projectBuildConfigurationList), StringValue("compatibilityVersion", "Xcode 3.2"), IdValue("developmentRegion", "English"), # new in Xcode 5 IntValue("hasScannedForEncodings", 1), ListValue("knownRegions", ["en"]), # new in Xcode 5 IdValue("mainGroup", group_root.key), # PBXGroup reference StringValue("projectDirPath", ""), StringValue("projectRoot", ""), IdValue("productRefGroup", products.key), ListValue("targets", [targetRef]), # Native Target refs #IdValue("productRefGroup", products) ListValue("projectReferences", project_ref_list) ] project = PBXProjectSection() project.children.append(ArrayKeyValue(projectRef, projectProperties)) objects.append(project) referenceProxySection = PBXReferenceProxySection() referenceProxySection.children.extend(reference_proxies) objects.append(referenceProxySection) resourcesProperties = [ IdValue("isa", "PBXResourcesBuildPhase"), IntValue("buildActionMask", 2147483647), ListValue("files", resources_references), IntValue("runOnlyForDeploymentPostprocessing", 0) ] resourcePhase = ArrayKeyValue(resourcesReference, resourcesProperties) resources = PBXResourcesBuildPhaseSection() resources.children.append(resourcePhase) objects.append(resources) if prebuild_commands: prebuild = PBXShellScriptBuildPhaseSection() processed_commands = self.cleanse_commands(prebuild_commands) properties = [ IdValue("isa", "PBXShellScriptBuildPhase"), IntValue("buildActionMask", 2147483647), ListValue("files", []), ListValue("inputPaths", []), IdValue("name", "Prebuild"), ListValue("outputPaths", []), IntValue("runOnlyForDeploymentPostprocessing", 0), IdValue("shellPath", "/bin/sh"), StringValue("shellScript", "\n".join(processed_commands)) ] prebuild.children.append(ArrayKeyValue(prebuildReference, properties)) objects.append(prebuild) sourcesProperties = [ IdValue("isa", "PBXSourcesBuildPhase"), IntValue("buildActionMask", 2147483647), ListValue("files", source_references), IntValue("runOnlyForDeploymentPostprocessing", 0), ] sources = PBXSourcesBuildPhaseSection() sources.children.append(ArrayKeyValue(sourcesReference, sourcesProperties)) objects.append(sources) # Better yet: #sources = PBXSourcesBuildPhase(files=source_references) #source_section = PBXSourcesBuildPhaseSection(sourcesReference, sources) target_dependency_section = PBXTargetDependencySection() target_dependency_section.children.extend(target_dependencies) objects.append(target_dependency_section) if postbuild_commands: processed_commands = self.cleanse_commands(postbuild_commands) postbuild = PBXShellScriptBuildPhaseSection() properties = [ IdValue("isa", "PBXShellScriptBuildPhase"), IntValue("buildActionMask", 2147483647), ListValue("files", []), ListValue("inputPaths", []), IdValue("name", "Postbuild"), ListValue("outputPaths", []), IntValue("runOnlyForDeploymentPostprocessing", 0), IdValue("shellPath", "/bin/sh"), StringValue("shellScript", "\n".join(processed_commands)) ] postbuild.children.append(ArrayKeyValue(postbuildReference, properties)) objects.append(postbuild) variant = PBXVariantGroupSection() variant.children = variant_groups objects.append(variant) build_configuration_section = XCBuildConfigurationSection() objects.append(build_configuration_section) # Per Layout variables target_configurations = [] product_configurations = [] for configuration in configurations: target_configuration_reference = PBXReference(comment=configuration) product_configuration_reference = PBXReference(comment=configuration) build_settings = [] for architecture in architectures: #product = base_product.target(configuration=configuration, architecture=architecture) params = context.get_layout(architecture, configuration) local_context = params.context product = local_context.product # Intentionally omit the architecture from the context update call. # Since this supports a universal binary, we don't specify a single architecture. # Variable replacement for "architecture", must be overridden so we specify the full list here. # This allows context's logic to turn this into something generic, because MacOS prefers fat binaries # and doesn't allow per architecture overrides for all their project settings. (product/obj dirs) #context.update(product=product, architecture=architectures, configuration=configuration) arch_map = XcodeArchitectureMap(architecture) subkey = "arch=%s" % arch_map #valid_archs = [self.arch_map[x] if self.arch_map.has_key(x) else x for x in architectures] valid_archs = [str(XcodeArchitectureMap(arch)) for arch in architectures] generic_settings = [] arch_is_non_standard = False arch = "$(ARCHS_STANDARD)" #logging.info("TODO: move these to MacOS X target platform!") if target_platform.matches("macosx"): if "i386" in valid_archs and "x86_64" in valid_archs: arch = "$(ARCHS_STANDARD_32_64_BIT)" arch_is_non_standard = True elif len(valid_archs) == 1 and valid_archs[0] == "i386": arch = "$(ARCHS_STANDARD_32_BIT)" arch_is_non_standard = True else: # We filter out this architecture (which doesn't make an explicit appearance) # And replace the subkey with "Any iOS Simulator SDK" if architecture == Architecture.x86: subkey = "sdk=iphonesimulator*" # ARCHS: architectures to which the binary is targeted if arch_is_non_standard: generic_settings.append(StringValue("ARCHS", arch)) # VALID_ARCHS: architectures for which the binary may be built #logging.info("TODO: move to macosx TargetPlatform") if target_platform.matches("macosx"): # remove i386 from the list if "i386" in valid_archs: valid_archs.remove("i386") # this is now setup to use the LIST, which will default to a generic folder # path, if it contains more than one item. local_context.update(architecture=architectures) generic_settings.append(StringValue("VALID_ARCHS", " ".join(valid_archs))) generic_settings.append(StringValue("PRODUCT_NAME", product_name)) generic_settings.append(StringValue("CONFIGURATION_BUILD_DIR", local_context.abs_product_root)) if params.object_root: # The project is created with a relative path to the object root. object_root = os.path.join(context.project_to_base_relative, local_context.object_root) generic_settings.append(StringValue("OBJROOT", object_root)) generic_settings.append(StringValue("CONFIGURATION_TEMP_DIR", object_root)) generic_settings.append(StringValue("SYMROOT", object_root)) # restore back to a single architecture local_context.update(architecture=architecture) # preprocess the links attribute other_ldflags = params.linkflags[:] other_cflags, warning_cflags = self.split_compiler_flags(params.cflags[:]) # GCC_PREPROCESSOR_DEFINITIONS takes care of this list #if product.defines: #other_cflags += [("-D%s" % x) for x in product.defines] other_cplusplusflags = params.cxxflags[:] # These have to be added as a PBXBuildFile and PBXFileReference. # The PBXBuildFile is added to a Frameworks group in the PBXFrameworksBuildPhase. # That Group is referenced by the PBXNativeTarget. # The PBXFileReference is added to PBXGroup "Frameworks". other_ldflags = params.driver.linkflags #local_frameworks = params.driver.frameworks #local_libraries = params.driver.libraries if other_ldflags: ldflags = [StringValue(x, None, key_only=True) for x in other_ldflags] build_settings.append(ListValue("OTHER_LDFLAGS", ldflags, subkey=subkey)) if params.defines: defines = [StringValue(x, None, key_only=True) for x in params.defines] build_settings.append(ListValue("GCC_PREPROCESSOR_DEFINITIONS", defines, subkey=subkey)) includes = make_paths_relative_to_project(context, params.includes) if includes: include_paths = [] for path in includes: include_paths.append(StringValue(path, None, key_only=True)) build_settings.append(ListValue("HEADER_SEARCH_PATHS", include_paths, subkey=subkey)) # "MACH_O_TYPE": ["mh_executable", "mh_dylib", "mh_bundle", "staticlib", "mh_object"] # "DEBUG_INFORMATION_FORMAT" : ["dwarf", "dwarf-with-dsym"] #libdirs = dependencies.libdirs + (product.libdirs if product.libdirs else []) if params.libdirs: lib_paths = [] for path in params.libdirs: lib_paths.append(StringValue(path, None, key_only=True)) build_settings.append(ListValue("LIBRARY_SEARCH_PATHS", lib_paths, subkey=subkey)) # allow the build scripts to override default variables for key,_ in self.driver_schema.iteritems(): value = getattr(params.driver, key, None) if value: #logging.info("DRIVER %s -> %s" % (key.upper(), value)) build_settings.append(StringValue(key.upper(), value)) #logging.info("TODO: Move CODE_SIGN_IDENTITY to platform: iphoneos") if target_platform.matches("iphoneos") or target_platform.matches("iphonesimulator"): build_settings.append(StringValue("CODE_SIGN_IDENTITY", "iPhone Developer")) # extra build settings for frameworks # FRAMEWORK_VERSION = A # DYLIB_COMPATIBILITY_VERSION = 1 # DYLIB_CURRENT_VERSION = 1 # WRAPPER_EXTENSION = framework build_settings.append(StringValue("OTHER_CFLAGS", " ".join(other_cflags))) build_settings.append(StringValue("OTHER_CPLUSPLUSFLAGS", " ".join(other_cplusplusflags))) build_settings.append(StringValue("WARNING_CFLAGS", " ".join(warning_cflags))) build_configuration = [ IdValue("isa", "XCBuildConfiguration"), IdValue("buildSettings", ArrayValue(None, generic_settings+build_settings)), StringValue("name", configuration) ] target_ref = ArrayKeyValue(target_configuration_reference, build_configuration) product_ref = ArrayKeyValue(product_configuration_reference, build_configuration) build_configuration_section.children.append(target_ref) build_configuration_section.children.append(product_ref) target_configurations.append(target_configuration_reference) product_configurations.append(product_configuration_reference) # Generate Scheme file for configuration scheme_format = "%(product_name)s - %(configuration)s" scheme_vars = { "product_name" : product_name, "configuration" : configuration } scheme_filename = "%s.xcscheme" % (scheme_format % scheme_vars) full_scheme_path = os.path.join(scheme_path, scheme_filename) scheme_file = filemanager.create(full_scheme_path) # Create a list of dependent references dependent_references = [] for dependency in context.ordered_dependencies: reference = target_ref_dict[dependency.name] dependent_references.append((dependency, reference)) write_xcode_scheme( self, scheme_file, project_name, projectRef, product, configuration, product.commandline, dependent_references ) configProperties = [ IdValue("isa", "XCConfigurationList"), ListValue("buildConfigurations", target_configurations), IntValue("defaultConfigurationIsVisible", 0), IdValue("defaultConfigurationName", configurations[0]) # Was StringValue, but Xcode converts this to IdValue ] targetConfigurations = ArrayKeyValue(nativeTargetConfiguration, configProperties) configProperties = [ IdValue("isa", "XCConfigurationList"), ListValue("buildConfigurations", product_configurations), IntValue("defaultConfigurationIsVisible", 0), IdValue("defaultConfigurationName", configurations[0]) # Was StringValue, but Xcode converts this to IdValue ] projectConfigurations = ArrayKeyValue(projectBuildConfigurationList, configProperties) # build it all together for the configuration list section buildConfigurationList = XCConfigurationListSection() buildConfigurationList.children.append(targetConfigurations) buildConfigurationList.children.append(projectConfigurations) objects.append(buildConfigurationList) ms.children.append(ArrayKeyValue("objects", objects)) ms.children.append(IdValue("rootObject", projectRef)) ms.visit(writer) if len(context_list) > 1: product_data = {} root = context_list[0].abs_root for context in context_list: product_data[context.product.name] = os.path.relpath(context.abs_project_root, context.abs_root) # compile the project name list relative_project_names = [] for key, value in product_data.iteritems(): xcodeproj_name = "%s.xcodeproj" % key project_path = os.path.join(value, xcodeproj_name) relative_project_names.append(project_path) # setup name, path and filename for workspace items workspace_name = "%s.xcworkspace" % context.buildfile.filename workspace_path = os.path.join(root, workspace_name) self.generate_workspace(executor, filemanager, relative_project_names, workspace_path)
def generate(self, **kwargs): executor = kwargs.get("executor", None) buildfile = kwargs.get("buildfile", None) filemanager = kwargs.get("filemanager", None) target_platform = kwargs.get("target_platform", None) context_list = kwargs.get("context_list", None) # TODO@apetrone: Should be able to pass the language type to # target_platform.compiler.get_command(). # base variables base_variables = { "CC" : target_platform.compiler.get_command(), "CXX" : "g++", "RM" : "rm -rf", "AR": "ar", "LD": "ld", "MAKE": "make", "MKDIR" : "mkdir -p" } for context in context_list: # fetch architecture and configurations architectures, configurations = buildfile.get_layout_params(context.product) makefile_parameters = get_parameters(architectures, configurations) arch_eq = MakefileIfEq(condition="$(arch),%s" % architectures[0], ifeq=["TARGET_ARCH = %s" % self.arch_to_flag(architectures[0])]) makefile_parameters.append(arch_eq) product_make = makefile_name_from_product(context.product) product_makefile_path = os.path.join(context.abs_project_root, product_make) handle = filemanager.create(product_makefile_path) writer = FileWriter(handle) product_name = context.product.name output = context.product.get("output") main_params = context.params_list[0] for configuration in configurations: product = context.get_layout(architectures[0], configuration) arch = VariableContext() arch.inherit(product) arch.update(architecture="${arch}", configuration="${config}") arch.object_root = product.get("object_root", expand=False) variables = deepcopy(base_variables) """ driver_data = product.driver for k,v in driver_data.iteritems(): if v: if type(v) is list: variables[k].extend(v) elif type(v) is dict: variables[k].update(v) elif type(v) is str or type(v) is unicode: variables[k] = v """ cppflags = self.driver_schema["cppflags"][:] cppflags.append("$(TARGET_ARCH)") cppflags.extend(product.cflags[:]) lower_cppflags = [flag.lower() for flag in cppflags] cxxflags = self.driver_schema["cxxflags"][:] cxxflags.append("$(CPPFLAGS)") cxxflags.extend(product.cxxflags[:]) ldflags = self.driver_schema["ldflags"][:] ldflags.append("$(TARGET_ARCH)") ldflags.extend(product.linkflags[:]) lower_ldflags = [flag.lower() for flag in ldflags] if output == ProductType.DynamicLibrary: if "-fpic" not in lower_cppflags: cppflags.append("-fPIC") if "-fpic" not in lower_ldflags: ldflags.append("-fPIC") if "-shared" not in lower_ldflags: ldflags.append("-shared") if product.defines: for flag in product.defines: cppflags.append("-D%s" % flag) includes = make_paths_relative_to_project(context, product.includes) for path in includes: cppflags.append("-I%s" % path) if product.libdirs: for path in product.libdirs: ldflags.append("-L%s" % path) links = product.links if product.links else [] ldflags.extend(links) values = [] for key,value in variables.iteritems(): values.append("%s = %s" % (key, value)) products_path = arch.abs_product_root or " " if arch.abs_product_root: products_path = ("%s/" % arch.abs_product_root) values.append("BINDIR = %s" % products_path) objdir = "${ROOT_PATH}/%s" % (arch.object_root) values.append("OBJDIR = %s" % objdir) values.append("TARGET = $(BINDIR)%s" % (target_platform.get_full_product_name(context.product))) values.append("CPPFLAGS = %s" % (" ".join(cppflags))) values.append("CXXFLAGS = %s" % (" ".join(cxxflags))) values.append("LDFLAGS = %s" % (" ".join(ldflags))) layout_condition = MakefileIfEq(condition="$(config),%s" % configuration, ifeq=values) makefile_parameters.append(layout_condition) # # Write the header of the Makefile makefile_write_header(writer) # discard all built-in make targets. writer.writeln(".SUFFIXES:") writer.writeln() # write out variables for parameter in makefile_parameters: parameter.write(writer) # write out phony targets writer.writeln(".PHONY: all clean help") writer.writeln() writer.writeln("ROOT_PATH=%s" % context.abs_root) writer.writeln() objects = MakefileArrayList(varname="OBJECTS") objdir_rules = [ "@echo Creating $(OBJDIR)", "$(SILENT) $(MKDIR) $(OBJDIR)" ] vpaths = MakefileArrayList(varname="VPATH") # organize sources into vobjects and objects. self.organize_sources(main_params.sources, context, vpaths, objects) vpaths.write(writer) writer.writeln() objects.write(writer) writer.writeln() writer.writeln("DEPENDS = $(OBJECTS:.o=.d)") writer.writeln() bindir = MakefileTarget(pattern="$(BINDIR)", rules=[ "@echo Creating $(BINDIR)", "$(SILENT) $(MKDIR) $(BINDIR)" ]) objdir = MakefileTarget(pattern="$(OBJDIR)", rules=objdir_rules) # prefix the commands with $(SILENT) -- the user probably doesn't want # to see these being executed. prebuild_commands = ["$(SILENT) %s" % x for x in product.prebuild_commands] postbuild_commands = ["$(SILENT) %s" % x for x in product.postbuild_commands] prebuild = MakefileTarget(name="prebuild", rules=prebuild_commands) if prebuild_commands else None postbuild = MakefileTarget(name="postbuild", rules=postbuild_commands) if postbuild_commands else None help = MakefileTarget(name="help", rules=[ "@echo Building project $(arch) - $(config)" ]) # get the appropriate linker linker = self.__class__.language_to_linker[product.determine_language()] # list of linkable products. linked_products = [ProductType.Application, ProductType.Commandline, ProductType.DynamicLibrary] if product.context.product.output in linked_products: target0 = MakefileTarget(pattern="$(TARGET)", rules=[ "@echo Linking %s" % product_name, "$(SILENT) $(%s) -o $(TARGET) $(OBJECTS) $(LDFLAGS)" % linker ], dependencies=[ objects ]) elif product.context.product.output == ProductType.StaticLibrary: # "r": replace or insert files into the archive # "s": act as ranlib; i.e. generate an archive index # "c": don't warn if the library had to be created # "v": verbose flags = ["r", "s", "c"] verbose = False if verbose: flags.append("v") target0 = MakefileTarget(pattern="$(TARGET)", rules=[ "@echo Creating Static Library for: %s..." % product_name, "$(SILENT) $(AR) %s $(TARGET) $(OBJECTS)" % ("".join(flags)) ], dependencies=[objects]) else: raise Exception("Unknown or unsupported ProductType: %s for product: %s" % (product.context.product.output, product.context.product.name)) c_file = MakefileTarget(pattern="%.c", rules=[]) compiling_rule = "@echo Compiling \"$<\" ..." c_target = MakefileTarget(pattern="$(OBJDIR)/%.o", rules=[ compiling_rule, "$(SILENT) $(CC) $(CPPFLAGS) -o \"$@\" -c \"$<\"" ], dependencies=[ c_file ]) cc_file = MakefileTarget(pattern="%.cc", rules=[]) cc_target = MakefileTarget(pattern="$(OBJDIR)/%.o", rules=[ compiling_rule, "$(SILENT) $(CXX) $(CXXFLAGS) -o \"$@\" -c \"$<\"" ], dependencies=[ cc_file ]) cpp_file = MakefileTarget(pattern="%.cpp", rules=[]) cpp_target = MakefileTarget(pattern="$(OBJDIR)/%.o", rules=[ compiling_rule, "$(SILENT) $(CXX) $(CXXFLAGS) -o \"$@\" -c \"$<\"" ], dependencies=[ cpp_file ]) # Here we construct the all_deps dynamically # because we want to omit the prebuild targets if they don't # contribute anything. all_deps = [bindir, objdir] if prebuild: all_deps.append(prebuild) all_deps.append(target0) if postbuild: all_deps.append(postbuild) all_target = MakefileTarget(name="all", rules=[ "@:", "@echo Done.", ], dependencies=all_deps) clean_target = MakefileTarget(name="clean", rules=[ "$(SILENT) $(RM) $(TARGET)", "$(SILENT) $(RM) $(OBJECTS)", "$(SILENT) $(RM) $(DEPENDS)" ]) new_targets = [all_target, help] if prebuild: new_targets.append(prebuild) if postbuild: new_targets.append(postbuild) new_targets.extend([target0, objdir, bindir, c_target, cc_target, cpp_target, clean_target]) # add targets for each target in our pegasus script for target in new_targets: target.write(writer) writer.writeln("-include $(DEPENDS)") writer.close() self.write_makefile(**kwargs) return None