def write_makefile(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) # In post generate, we create a top level "Makefile", which contains targets for each of # the sub make files -- which is what "generate" does for each product. # The top-level Makefile can be used to make each product: # e.g. make squirrel context = context_list[0] product = context.product root = context.abs_root # # Preprocess step to create data for each product # # create a list of obj dirs objdirs = [] # create a list of lib/bin dirs. bindirs = [] # create a list of targets. targets = [] # product names which can be used manually on the commandline product_names = [] architectures, configurations = buildfile.get_layout_params(product) makefile_parameters = get_parameters(architectures, configurations) phony_targets = [ "all", "clean" ] target_clean_rules = [] for context in context_list: mk = VariableContext() mk.inherit(context) mk.product_root = context.get("product_root", expand=False) mk.object_root = context.get("object_root", expand=False) mk.update(architecture="${arch}", configuration="${config}") if mk.object_root not in objdirs: objdirs.append(mk.object_root) if mk.product_root not in bindirs: bindirs.append(mk.product_root) # add product name product_names.append(context.product.name) # convert the dependencies for this product into a list # of MakefileTargets dependencies = [] for instance in context.ordered_dependencies: dependencies.append(MakefileArrayList(name=instance.name)) # setup targets makefile_path = makefile_name_from_product(context.product) target = MakefileTarget(name=context.product.name, rules=[ "$(SILENT) $(MAKE) --no-print-directory -C %s -f %s" % (context.base_to_project_relative, makefile_path) ], dependencies = dependencies) clean_rule = "$(SILENT) $(MAKE) --no-print-directory -C %s -f %s clean" % (context.base_to_project_relative, makefile_path) target_clean_rules.append(clean_rule) targets.append(target) # variables the makefile accepts as parameters. variables = ["arch", "config", "verbose"] # script variables script_vars = { "objdir" : objdirs, "bindir" : bindirs, "targets" : [t.get_output_name() for t in targets] } clean = MakefileTarget(name="clean", rules=target_clean_rules) # # Write out the actual Makefile with data from above. # makefile_path = os.path.join(root, "Makefile") handle = filemanager.create(makefile_path) writer = FileWriter(handle) # # Write the header of the Makefile makefile_write_header(writer) # # export all variables # for parameter in makefile_parameters: parameter.write(writer) # setup variables used by this script for key, value in script_vars.iteritems(): writer.writeln("%s=%s" % (key.upper(), " ".join(value))) writer.writeln() phony_targets.extend(product_names) # write phony and all targets writer.writeln(".PHONY: %s" % " ".join(phony_targets)) writer.writeln() writer.writeln("all: %s" % " ".join(product_names)) writer.writeln() # write actual targets, which call the product's own makefile for t in targets: t.write(writer) # write clean target clean.write(writer)
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 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