Exemplo n.º 1
0
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)
Exemplo n.º 2
0
	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)
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
	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)
Exemplo n.º 5
0
	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