Example #1
0
String AsXML(const XmlNode& node, dword style)
{
	StringBuffer r;
	if(style & XML_HEADER)
		r << XmlHeader();
	if(style & XML_DOCTYPE)
		for(int i = 0; i < node.GetCount(); i++) {
			const XmlNode& m = node.Node(i);
			if(m.GetType() == XML_TAG) {
				r << XmlDocType(m.GetText());
				break;
			}
		}
	style &= ~(XML_HEADER|XML_DOCTYPE);
	switch(node.GetType()) {
	case XML_PI:
		r << "<?" << node.GetText() << "?>\r\n";
		break;
	case XML_DECL:
		r << "<!" << node.GetText() << ">\r\n";
		break;
	case XML_COMMENT:
		r << "<!--" << node.GetText() << "-->\r\n";
		break;
	case XML_DOC:
		for(int i = 0; i < node.GetCount(); i++)
			r << AsXML(node.Node(i), style);
		break;
	case XML_TEXT:
		r << DeXml(node.GetText());
		break;
	case XML_TAG:
		XmlTag tag(node.GetText());
		for(int i = 0; i < node.GetAttrCount(); i++)
			tag(node.AttrId(i), node.Attr(i));
		if(node.GetCount()) {
			StringBuffer body;
			for(int i = 0; i < node.GetCount(); i++)
				body << AsXML(node.Node(i), style);
			r << tag(~body);
		}
		else
			r << tag();
	}
	return r;
}
bool MSBuild_ProjectFile::Generate(
	DatabaseFile& databaseFile,
	WorkspaceFile& workspaceFile,
	ProjectFile& projectFile,
	IdeHelper::BuildProjectMatrix& buildMatrix
)
{
	Platform::Path solutionDirectory =
		workspaceFile.Get_Workspace_Location();

	Platform::Path projectDirectory =
		projectFile.Get_Project_Location();

	Platform::Path projectLocation =
		projectDirectory.AppendFragment(
			projectFile.Get_Project_Name() + ".vcxproj", true);

	std::vector<std::string> configurations =
		workspaceFile.Get_Configurations_Configuration();

	std::vector<EPlatform> platforms =
		workspaceFile.Get_Platforms_Platform();

	std::string projectGuid = Strings::Guid({
		workspaceFile.Get_Workspace_Name(),
		projectFile.Get_Project_Name() });

	// Files.
	std::vector<std::string> allFiles;

	std::vector<Platform::Path> baseFiles = projectFile.Get_Files_File();
	for (Platform::Path& path : baseFiles)
	{
		std::string relativePath = "$(SolutionDir)" +
			solutionDirectory.RelativeTo(path).ToString();

		allFiles.push_back(relativePath);
	}

	XmlNode root;

	// Header
	root.Node("?xml")
		.Attribute("version", "1.0")
		.Attribute("encoding", "utf-8");

	XmlNode& project =
		root.Node("Project")
		.Attribute("DefaultTargets", "Build")
		.Attribute("ToolsVersion", "")
		.Attribute("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003");

	// Build Matrix
	XmlNode& projectConfig =
		project.Node("ItemGroup")
		.Attribute("Label", "ProjectConfiguration");

	for (auto matrix : buildMatrix)
	{
		std::string platformId = MSBuild::GetPlatformID(matrix.platform);

		XmlNode& buildConfig =
			projectConfig.Node("ProjectConfiguration")
			.Attribute("Include", "%s|%s", matrix.config.c_str(), platformId.c_str());

		buildConfig.Node("Configuration").Value("%s", matrix.config.c_str());
		buildConfig.Node("Platform").Value("%s", platformId.c_str());
	}

	// Globals.
	XmlNode& globals =
		project.Node("PropertyGroup")
		.Attribute("Label", "Globals");

	globals.Node("ProjectGuid").Value("%s", projectGuid.c_str());
	globals.Node("Keyword").Value("MakeFileProj");

	// Undocumented hackery going on here:
	// As we have our own custom platforms, and we are building them through makefiles we are just
	// going to silence the "platform not found" warnings visual studio gives. It seems preferable
	// to creating a bunch of unneccessary project property files and tampering with visual studio.
	globals.Node("PlatformTargetsFound").Value("True");
	globals.Node("PlatformPropsFound").Value("True");	 
	globals.Node("ToolsetTargetsFound").Value("True");
		
	// Imports
	project.Node("Import")
		.Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props");

	// Property Grid
	for (auto matrix : buildMatrix)
	{
		std::string platformId = MSBuild::GetPlatformID(matrix.platform);

		XmlNode& propertyGroup =
			project.Node("PropertyGroup")
			.Attribute("Condition", "'$(Configuration)|$(Platform)'=='%s|%s'", matrix.config.c_str(), platformId.c_str())
			.Attribute("Label", "Configuration");

		propertyGroup.Node("ConfigurationType").Value("Makefile");

		// Debug libraries.
		if (matrix.projectFile.Get_Build_OptimizationLevel() == EOptimizationLevel::None ||
			matrix.projectFile.Get_Build_OptimizationLevel() == EOptimizationLevel::Debug)
		{
			propertyGroup.Node("UseDebugLibraries").Value("true");
		}
		else
		{
			propertyGroup.Node("UseDebugLibraries").Value("false");
		}

		// Platform tooltype.
		switch (matrix.projectFile.Get_Build_PlatformToolset())
		{
		case EPlatformToolset::Default:
			propertyGroup.Node("PlatformToolset").Value("%s", CastToString(m_defaultToolset).c_str());
			break;
		case EPlatformToolset::MSBuild_v140:
			propertyGroup.Node("PlatformToolset").Value("v140");
			break;
		case EPlatformToolset::MSBuild_v141:
			propertyGroup.Node("PlatformToolset").Value("v141");
			break;
		default:
			// Anny others will be dealt with on a per-platform basis, eg, AnyCPU etc.
			break;
		}
	}

	// Imports
	project.Node("Import")
		.Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props");

	// Extension settings
	project.Node("ImportGroup")
		.Attribute("Label", "ExtensionSettings");

	// Shared settings
	project.Node("ImportGroup")
		.Attribute("Label", "Shared");

	// Property sheets
	XmlNode& propSheetsNode = 
		project.Node("ImportGroup")
		.Attribute("Label", "PropertySheets");

	propSheetsNode.Node("Import")
		.Attribute("Project", "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props")
		.Attribute("Label", "LocalAppDataPlatform")
		.Attribute("Condition", "Exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')");
	
	propSheetsNode.Node("Import")
		.Attribute("Project", "$(UserRootDir)\\Microsoft.Cpp.Win32.user.props")
		.Attribute("Label", "LocalAppDataPlatform")
		.Attribute("Condition", "!Exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')");
	
	// Macros
	project.Node("ImportGroup")
		.Attribute("Label", "UserMacros");

	// NMake property groups.
	for (auto matrix : buildMatrix)
	{
		std::string platformId = MSBuild::GetPlatformID(matrix.platform);

		Platform::Path outDir = matrix.projectFile.Get_Project_OutputDirectory();
		Platform::Path intDir = matrix.projectFile.Get_Project_IntermediateDirectory();

		Platform::Path outDirRelative = solutionDirectory.RelativeTo(outDir);
		Platform::Path intDirRelative = solutionDirectory.RelativeTo(intDir);

		std::vector<std::string> forcedIncludes;
		std::vector<std::string> includePaths;

		std::vector<std::string> defines = matrix.projectFile.Get_Defines_Define();

		XmlNode& propertyGroup =
			project.Node("PropertyGroup")
			.Attribute("Condition", "'$(Configuration)|$(Platform)'=='%s|%s'", matrix.config.c_str(), platformId.c_str());

		for (Platform::Path& path : matrix.projectFile.Get_ForcedIncludes_ForcedInclude())
		{
			forcedIncludes.push_back("$(SolutionDir)\\" + solutionDirectory.RelativeTo(path).ToString());
		}

		for (Platform::Path& path : matrix.projectFile.Get_SearchPaths_IncludeDirectory())
		{
			Platform::Path relativePath = solutionDirectory.RelativeTo(path).ToString();
			if (relativePath.IsRelative())
			{
				includePaths.push_back("$(SolutionDir)\\" + relativePath.ToString() + "\\");
			}
			else
			{
				includePaths.push_back(relativePath.ToString());
			}
		}

		Platform::Path relativeMicroBuildPath = solutionDirectory.RelativeTo(Platform::Path::GetExecutablePath());

		propertyGroup.Node("NMakeBuildCommandLine").Value("%s Build %s %s -c=%s -p=%s --silent", 
			Strings::Quoted("$(SolutionDir)\\" + relativeMicroBuildPath.ToString()).c_str(), 
			Strings::Quoted(workspaceFile.Get_Workspace_File().ToString()).c_str(),
			projectFile.Get_Project_Name().c_str(),
			matrix.config.c_str(),
			CastToString(matrix.platform).c_str()
		);
		propertyGroup.Node("NMakeOutput").Value("$(SolutionDir)%s\\%s%s", outDirRelative.ToString().c_str(), matrix.projectFile.Get_Project_OutputName().c_str(), matrix.projectFile.Get_Project_OutputExtension().c_str());
		propertyGroup.Node("NMakeCleanCommandLine").Value("%s Clean %s %s -c=%s -p=%s --silent",
			Strings::Quoted("$(SolutionDir)\\" + relativeMicroBuildPath.ToString()).c_str(), 
			Strings::Quoted(workspaceFile.Get_Workspace_File().ToString()).c_str(),
			projectFile.Get_Project_Name().c_str(),
			matrix.config.c_str(),
			CastToString(matrix.platform).c_str()
		);
		propertyGroup.Node("NMakeReBuildCommandLine").Value("%s Build %s %s -c=%s -p=%s -r --silent",
			Strings::Quoted("$(SolutionDir)\\" + relativeMicroBuildPath.ToString()).c_str(), 
			Strings::Quoted(workspaceFile.Get_Workspace_File().ToString()).c_str(),
			projectFile.Get_Project_Name().c_str(),
			matrix.config.c_str(),
			CastToString(matrix.platform).c_str()
		);
		propertyGroup.Node("NMakePreprocessorDefinitions").Value("%s;$(NMakePreprocessorDefinitions)", Strings::Join(defines, ";").c_str());
		propertyGroup.Node("NMakeIncludeSearchPath").Value("%s;$(NMakeIncludeSearchPath)", Strings::Join(includePaths, ";").c_str());
		propertyGroup.Node("NMakeForcedIncludes").Value("%s;$(NMakeForcedIncludes)", Strings::Join(forcedIncludes, ";").c_str());
	}

	// Empty item group (nmake project seems to generate this by default?).
	project.Node("ItemDefinitionGroup");

	// Create list of files.
	XmlNode& groupNode = 
		project.Node("ItemGroup");

	for (auto file : allFiles)
	{
		groupNode.Node("None")
			.Attribute("Include", "%s", file.c_str());
	}
		
	// We store which groups each file goes into so we can easily
	// generate a filters file.
	std::vector<MSBuildFileGroup> fileGroupList;

	MSBuildFileGroup group;
	for (auto file : allFiles)
	{
		MSBuildFile groupFile;
		groupFile.TypeId = "None";
		groupFile.Path = file;
		group.Files.push_back(groupFile);
	}
	fileGroupList.push_back(group);

	// Imports
	project.Node("Import")
		.Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets");

	// Extension settings
	project.Node("ImportGroup")
		.Attribute("Label", "ExtensionSettings");

	// Generate result.
	if (!databaseFile.StoreFile(
		workspaceFile,
		projectLocation,
		root.ToString().c_str()))
	{
		return false;
	}

	// Generate the filters file.
	MSBuild_VcxFiltersFile filtersFile(m_defaultToolsetString);

	if (!filtersFile.Generate(
		databaseFile,
		workspaceFile,
		projectFile,
		buildMatrix,
		fileGroupList
	))
	{
		return false;
	}

	return true;
}