public static void GenerateBuildScriptXCode(Cpp.OperatingSystemType HostOperatingSystem, PathString BuildDirectory, Cpp.ConfigurationType Configuration, int MaxProcessCount, List <String> CppSortedProjectNames, bool ForceRegenerate) { var Lines = new List <String>(); Lines.Add("#!/bin/bash"); Lines.Add("set -e"); foreach (var CppSortedProjectName in CppSortedProjectNames) { Lines.Add($"xcodebuild -project projects/{CppSortedProjectName}.xcodeproj -configuration {Configuration} -jobs {MaxProcessCount}"); } Lines.Add(""); var BuildPath = BuildDirectory / "build.sh"; TextFile.WriteToFile(BuildPath, String.Join("\n", Lines), new System.Text.UTF8Encoding(false), !ForceRegenerate); if (HostOperatingSystem != Cpp.OperatingSystemType.Windows) { if (Shell.Execute("chmod", "+x", BuildPath) != 0) { throw new InvalidOperationException("ErrorInExecution: chmod"); } } }
public void Generate(bool EnableRebuild) { var PbxprojPath = Path.Combine(OutputDirectory, Path.Combine(SolutionName + ".xcodeproj", "project.pbxproj")); var BaseDirPath = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(PbxprojPath))); var p = Plist.FromString(PbxprojTemplateText); var Objects = p.Dict["objects"].Dict; var RootObjectKey = p.Dict["rootObject"].String; var RootObject = Objects[RootObjectKey].Dict; ObjectReferenceValidityTest(Objects, RootObjectKey); RemoveFiles(Objects, RootObject["mainGroup"].String); var Targets = RootObject["targets"].Array; foreach (var TargetKey in Targets) { foreach (var PhaseKey in Objects[TargetKey.String].Dict["buildPhases"].Array) { var Phase = Objects[PhaseKey.String].Dict; var Type = Phase["isa"].String; if (Type == "PBXSourcesBuildPhase") { var Files = Phase["files"]; var ToBeRemoved = new HashSet <String>(); foreach (var FileKey in Files.Array) { var File = Objects[FileKey.String].Dict; var FileType = File["isa"].String; if (FileType == "PBXBuildFile") { var FileRef = File["fileRef"].String; if (!Objects.ContainsKey(FileRef)) { ToBeRemoved.Add(FileKey.String); Objects.Remove(FileKey.String); } } } if (ToBeRemoved.Count > 0) { Files.Array = Files.Array.Where(FileKey => !ToBeRemoved.Contains(FileKey.String)).ToList(); } } Objects.Remove(PhaseKey.String); } var ListKey = Objects[TargetKey.String].Dict["buildConfigurationList"]; foreach (var BuildConfigurationKey in Objects[ListKey.String].Dict["buildConfigurations"].Array) { Objects.Remove(BuildConfigurationKey.String); } Objects.Remove(ListKey.String); Objects.Remove(TargetKey.String); } RootObject["attributes"].Dict["TargetAttributes"].Dict.Clear(); RootObject["targets"].Array.Clear(); RootObject["productRefGroup"] = RootObject["mainGroup"]; ObjectReferenceValidityTest(Objects, RootObjectKey); var RelativePathToObjects = new Dictionary <String, String>(); foreach (var Project in ProjectReferences) { var RelativePath = Path.Combine(Project.VirtualDir, Project.Name); var Added = AddProject(Objects, RootObject["mainGroup"].String, new LinkedList <String>(), new LinkedList <String>(GetPathParts(RelativePath)), Project, BaseDirPath, RelativePathToObjects); if (!Added) { throw new InvalidOperationException(); } } ObjectReferenceValidityTest(Objects, RootObjectKey); TextFile.WriteToFile(PbxprojPath, Plist.ToString(p), new UTF8Encoding(false), !EnableRebuild); }
public void Generate(bool EnableRebuild) { var s = new SlnFile(); s.FullPath = Path.GetFullPath(Path.Combine(OutputDirectory, SolutionName + ".sln")); using (var sr = new StringReader(SlnTemplateText)) { s.Read(sr); } s.Projects.Clear(); s.ProjectConfigurationsSection.Clear(); SlnSection NestedProjects = null; foreach (var Section in s.Sections.Where(Section => Section.Id == "NestedProjects")) { Section.Clear(); NestedProjects = Section; } if (NestedProjects == null) { NestedProjects = new SlnSection { Id = "NestedProjects" }; s.Sections.Add(NestedProjects); } var Filters = new Dictionary <String, String>(StringComparer.OrdinalIgnoreCase); foreach (var Project in ProjectReferences) { var Dir = Project.VirtualDir.Replace('/', '\\'); if (!Filters.ContainsKey(Dir)) { var CurrentDir = Dir; while ((CurrentDir != "") && !Filters.ContainsKey(CurrentDir)) { var g = Guid.ParseExact(Hash.GetHashForPath(CurrentDir, 32), "N").ToString().ToUpper(); Filters.Add(CurrentDir, g); CurrentDir = Path.GetDirectoryName(CurrentDir); if (CurrentDir != "") { var gUpper = Guid.ParseExact(Hash.GetHashForPath(CurrentDir, 32), "N").ToString().ToUpper(); NestedProjects.Properties.SetValue("{" + g + "}", "{" + gUpper + "}"); } } } s.Projects.Add(new SlnProject { TypeGuid = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}", Name = Project.Name, FilePath = FileNameHandling.GetRelativePath(Project.FilePath, OutputDirectory), Id = "{" + Project.Id + "}" }); var conf = new SlnPropertySet("{" + Project.Id + "}"); foreach (var c in s.SolutionConfigurationsSection) { var Value = c.Value.Replace("|x86", "|Win32"); conf.SetValue(c.Key + ".ActiveCfg", Value); conf.SetValue(c.Key + ".Build.0", Value); } s.ProjectConfigurationsSection.Add(conf); NestedProjects.Properties.SetValue("{" + Project.Id + "}", "{" + Filters[Dir] + "}"); } foreach (var f in Filters) { s.Projects.Add(new SlnProject { TypeGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}", Name = Path.GetFileName(f.Key), FilePath = Path.GetFileName(f.Key), Id = "{" + f.Value + "}" }); } foreach (var Section in s.Sections.Where(Section => Section.Id == "ExtensibilityGlobals")) { Section.Properties.SetValue("SolutionGuid", "{" + SolutionId.ToUpper() + "}"); } String Text; using (var sw = new StringWriter()) { s.Write(sw); Text = sw.ToString(); } TextFile.WriteToFile(s.FullPath, Text, Encoding.UTF8, !EnableRebuild); }
public static void Run(Shell.EnvironmentVariableMemory Memory, bool Quiet, Variables v) { BuildScript.GenerateRetypemakeScript(v.HostOperatingSystem, v.SourceDirectory, v.BuildDirectory, Memory, v.OverwriteRetypemakeScript); var r = v.g(); var CppSortedProjectNames = r.SortedProjects.Where(p => (p.TargetType == Cpp.TargetType.Executable) || (p.TargetType == Cpp.TargetType.StaticLibrary) || (p.TargetType == Cpp.TargetType.DynamicLibrary) || (p.TargetType == Cpp.TargetType.DarwinApplication) || (p.TargetType == Cpp.TargetType.DarwinStaticFramework) || (p.TargetType == Cpp.TargetType.DarwinSharedFramework) || (p.TargetType == Cpp.TargetType.MacBundle)).Select(p => p.Name).ToList(); var GradleProjectNames = r.SortedProjects.Where(p => (p.TargetType == Cpp.TargetType.GradleApplication) || (p.TargetType == Cpp.TargetType.GradleLibrary)).Select(p => p.Name).ToList(); if (v.TargetOperatingSystem == Cpp.OperatingSystemType.Windows) { BuildScript.GenerateBuildScriptWindows(v.Toolchain, v.BuildDirectory, r.SolutionName, v.TargetArchitecture, Cpp.ConfigurationType.Debug, v.MaxProcessCount, v.VSDir, v.VSVersion, v.Ninja, v.ForceRegenerate); BuildScript.GenerateBuildScriptWindows(v.Toolchain, v.BuildDirectory, r.SolutionName, v.TargetArchitecture, Cpp.ConfigurationType.Release, v.MaxProcessCount, v.VSDir, v.VSVersion, v.Ninja, v.ForceRegenerate); if (v.BuildNow) { if (v.HostOperatingSystem == Cpp.OperatingSystemType.Windows) { using (var d = Shell.PushDirectory(v.BuildDirectory)) { if (Shell.Execute($"build_{v.Configuration}.cmd") != 0) { throw new InvalidOperationException("ErrorInExecution: " + $"build_{v.Configuration}.cmd"); } } } else { WriteLineError("Cross compiling to Windows is not supported."); } } } else if (v.TargetOperatingSystem == Cpp.OperatingSystemType.Linux) { BuildScript.GenerateBuildScriptLinux(v.TargetOperatingSystemDistribution, v.Toolchain, v.HostOperatingSystem, v.BuildDirectory, v.Configuration, v.MaxProcessCount, v.Ninja, v.ForceRegenerate); if (v.BuildNow) { using (var d = Shell.PushDirectory(v.BuildDirectory)) { if (v.HostOperatingSystem == Cpp.OperatingSystemType.Windows) { if (Shell.Execute(@".\build.cmd") != 0) { throw new InvalidOperationException("ErrorInExecution: " + @".\build.cmd"); } } else if (v.HostOperatingSystem == Cpp.OperatingSystemType.Linux) { if (Shell.Execute("./build.sh") != 0) { throw new InvalidOperationException("ErrorInExecution: ./build.sh"); } } else { WriteLineError("Cross compiling to Linux is not supported."); } } } } else if (v.TargetOperatingSystem == Cpp.OperatingSystemType.MacOS) { if (v.Toolchain == Cpp.ToolchainType.XCode) { BuildScript.GenerateBuildScriptXCode(v.HostOperatingSystem, v.BuildDirectory, v.Configuration, v.MaxProcessCount, CppSortedProjectNames, v.ForceRegenerate); } else { BuildScript.GenerateBuildScriptLinux("Mac", v.Toolchain, v.HostOperatingSystem, v.BuildDirectory, v.Configuration, v.MaxProcessCount, v.Ninja, v.ForceRegenerate); } if (v.BuildNow) { using (var d = Shell.PushDirectory(v.BuildDirectory)) { if (v.HostOperatingSystem == Cpp.OperatingSystemType.MacOS) { if (Shell.Execute("./build.sh") != 0) { throw new InvalidOperationException("ErrorInExecution: ./build.sh"); } } else { WriteLineError("Cross compiling to Mac is not supported."); } } } } else if (v.TargetOperatingSystem == Cpp.OperatingSystemType.iOS) { BuildScript.GenerateBuildScriptXCode(v.HostOperatingSystem, v.BuildDirectory, v.Configuration, v.MaxProcessCount, CppSortedProjectNames, v.ForceRegenerate); if (v.BuildNow) { using (var d = Shell.PushDirectory(v.BuildDirectory)) { if (v.HostOperatingSystem == Cpp.OperatingSystemType.MacOS) { if (Shell.Execute("./build.sh") != 0) { throw new InvalidOperationException("ErrorInExecution: ./build.sh"); } } else { WriteLineError("Cross compiling to iOS is not supported."); } } } } else if (v.TargetOperatingSystem == Cpp.OperatingSystemType.Android) { if (v.Toolchain != Cpp.ToolchainType.Ninja) { TextFile.WriteToFile(v.BuildDirectory / "gradle/local.properties", $"sdk.dir={v.AndroidSdk.ToString(PathStringStyle.Unix)}", new System.Text.UTF8Encoding(false), !v.ForceRegenerate); } BuildScript.GenerateBuildScriptAndroid(GradleProjectNames, v.Toolchain, v.HostOperatingSystem, v.BuildDirectory, v.TargetArchitecture, v.Configuration, v.MaxProcessCount, v.AndroidNdk, v.Ninja, 21, v.ForceRegenerate, v.EnableJava); if (v.BuildNow) { using (var d = Shell.PushDirectory(v.BuildDirectory)) { if (v.HostOperatingSystem == Cpp.OperatingSystemType.Windows) { if (Shell.Execute(@".\build.cmd") != 0) { throw new InvalidOperationException("ErrorInExecution: " + @".\build.cmd"); } } else if ((v.HostOperatingSystem == Cpp.OperatingSystemType.Linux) || (v.HostOperatingSystem == Cpp.OperatingSystemType.MacOS) || (v.HostOperatingSystem == Cpp.OperatingSystemType.Android)) { if (Shell.Execute("./build.sh") != 0) { throw new InvalidOperationException("ErrorInExecution: ./build.sh"); } } else { WriteLineError("Cross compiling to Android is not supported."); } } } } else { throw new InvalidOperationException(); } }
public static void GenerateRetypemakeScript(Cpp.OperatingSystemType HostOperatingSystem, PathString SourceDirectory, PathString BuildDirectory, Shell.EnvironmentVariableMemory Memory, bool OverwriteRetypemakeScript) { if (HostOperatingSystem == Cpp.OperatingSystemType.Windows) { var Lines = new List <String>(); Lines.Add("@echo off"); Lines.Add(""); Lines.Add("setlocal"); Lines.Add("if \"%SUB_NO_PAUSE_SYMBOL%\"==\"1\" set NO_PAUSE_SYMBOL=1"); Lines.Add("if /I \"%COMSPEC%\" == %CMDCMDLINE% set NO_PAUSE_SYMBOL=1"); Lines.Add("set SUB_NO_PAUSE_SYMBOL=1"); Lines.Add("call :main"); Lines.Add("set EXIT_CODE=%ERRORLEVEL%"); Lines.Add("if not \"%NO_PAUSE_SYMBOL%\"==\"1\" pause"); Lines.Add("exit /b %EXIT_CODE%"); Lines.Add(""); Lines.Add(":main"); foreach (var p in Memory.Variables) { if (p.Key == "BuildDirectory") { Lines.Add("set BuildDirectory=%~dp0"); } else { if (Memory.VariableSelections.ContainsKey(p.Key)) { Lines.Add($":: {String.Join("|", Memory.VariableSelections[p.Key])}"); } if (Memory.VariableMultipleSelections.ContainsKey(p.Key)) { Lines.Add($":: {String.Join(" ", Memory.VariableMultipleSelections[p.Key])}"); } Lines.Add($"set " + Shell.EscapeArgumentForShell(p.Key + "=" + p.Value, Shell.ShellArgumentStyle.CMD)); } } Lines.Add("pushd \"%SourceDirectory%\" || exit /b 1"); Lines.Add("call .\\typemake.cmd %* || exit /b 1 & popd & exit /b 0"); //all commands after typemake need to be in one line; or it may cause trouble when the file is changed by typemake Lines.Add(""); var RetypemakePath = BuildDirectory / "retypemake.cmd"; if (OverwriteRetypemakeScript || !File.Exists(RetypemakePath)) { TextFile.WriteToFile(RetypemakePath, String.Join("\r\n", Lines), System.Text.Encoding.Default, false); } else { WriteLineError("Retypemake script exists, script generation skipped."); } } else { var Lines = new List <String>(); Lines.Add("#!/bin/bash"); Lines.Add("set -e"); foreach (var p in Memory.Variables) { if (p.Key == "BuildDirectory") { Lines.Add("export BuildDirectory=$(cd `dirname \"$0\"`; pwd)"); } else { if (Memory.VariableSelections.ContainsKey(p.Key)) { Lines.Add($"# {String.Join("|", Memory.VariableSelections[p.Key])}"); } if (Memory.VariableMultipleSelections.ContainsKey(p.Key)) { Lines.Add($"# {String.Join(" ", Memory.VariableMultipleSelections[p.Key])}"); } Lines.Add($"export {p.Key}={Shell.EscapeArgumentForShell(p.Value, Shell.ShellArgumentStyle.Bash)}"); } } Lines.Add("pushd \"${SourceDirectory}\""); Lines.Add("./typemake.sh \"$@\"; popd; exit"); //all commands after typemake need to be in one line; or it may cause trouble when the file is changed by typemake Lines.Add(""); var RetypemakePath = BuildDirectory / "retypemake.sh"; if (OverwriteRetypemakeScript || !File.Exists(RetypemakePath)) { TextFile.WriteToFile(RetypemakePath, String.Join("\n", Lines), new System.Text.UTF8Encoding(false), false); if (Shell.Execute("chmod", "+x", RetypemakePath) != 0) { throw new InvalidOperationException("ErrorInExecution: chmod"); } } else { WriteLineError("Retypemake script exists, script generation skipped."); } } }
public static void GenerateBuildScriptAndroid(List <String> GradleProjectNames, Cpp.ToolchainType Toolchain, Cpp.OperatingSystemType HostOperatingSystem, PathString BuildDirectory, Cpp.ArchitectureType TargetArchitecture, Cpp.ConfigurationType Configuration, int MaxProcessCount, PathString AndroidNdk, PathString Ninja, int ApiLevel, bool ForceRegenerate, bool EnableJava) { if ((Toolchain == Cpp.ToolchainType.Gradle_Ninja) || (Toolchain == Cpp.ToolchainType.Ninja)) { if (HostOperatingSystem == Cpp.OperatingSystemType.Windows) { var Lines = new List <String>(); Lines.Add("@echo off"); Lines.Add(""); Lines.Add("setlocal"); Lines.Add("if \"%SUB_NO_PAUSE_SYMBOL%\"==\"1\" set NO_PAUSE_SYMBOL=1"); Lines.Add("if /I \"%COMSPEC%\" == %CMDCMDLINE% set NO_PAUSE_SYMBOL=1"); Lines.Add("set SUB_NO_PAUSE_SYMBOL=1"); Lines.Add("call :main"); Lines.Add("set EXIT_CODE=%ERRORLEVEL%"); Lines.Add("if not \"%NO_PAUSE_SYMBOL%\"==\"1\" pause"); Lines.Add("exit /b %EXIT_CODE%"); Lines.Add(""); Lines.Add(":main"); Lines.Add(Shell.EscapeArgumentForShell(Ninja.RelativeTo(BuildDirectory).ToString(PathStringStyle.Windows), Shell.ShellArgumentStyle.CMD) + $" -j{MaxProcessCount} -C projects -f build.ninja || exit /b 1"); if (EnableJava) { if (Toolchain == Cpp.ToolchainType.Gradle_Ninja) { Lines.Add("pushd gradle || exit /b 1"); Lines.Add($@"call .\gradlew.bat --no-daemon assemble{Configuration} || exit /b 1"); Lines.Add("popd"); Lines.Add("echo To debug a APK, open gradle directory in Android Studio, select Run - Edit Configurations - Debugger - Debug type, and change its value to Dual"); } else { foreach (var GradleProjectName in GradleProjectNames) { Lines.Add($"pushd batch\\{Shell.EscapeArgumentForShell(GradleProjectName.Split(':').First(), Shell.ShellArgumentStyle.CMD)} || exit /b 1"); Lines.Add("call build.cmd || exit /b 1"); Lines.Add("popd"); } Lines.Add("echo To debug a APK, open Android Studio, select Profile or debug APK and select the generated APK, close Android Studio and copy the APK debug project under UserDirectory/ApkProjects to where the APK located(ensure the overwrite of APK), open the new project in Android Studio, add debug symbols for .so files, attach sources for Java files, select Run - Edit Configurations - Debugger - Debug type, and change its value to Dual"); } } Lines.Add(""); var BuildPath = BuildDirectory / "build.cmd"; TextFile.WriteToFile(BuildPath, String.Join("\r\n", Lines), System.Text.Encoding.Default, !ForceRegenerate); } else { var Lines = new List <String>(); Lines.Add("#!/bin/bash"); Lines.Add("set -e"); Lines.Add(Shell.EscapeArgumentForShell(Ninja.RelativeTo(BuildDirectory).ToString(PathStringStyle.Unix), Shell.ShellArgumentStyle.Bash) + $" -j{MaxProcessCount} -C projects -f build.ninja"); if (EnableJava) { if (Toolchain == Cpp.ToolchainType.Gradle_Ninja) { Lines.Add("pushd gradle"); Lines.Add($@"./gradlew --no-daemon assemble{Configuration}"); Lines.Add("popd"); Lines.Add("echo To debug a APK, open gradle directory in Android Studio, select Run - Edit Configurations - Debugger - Debug type, and change its value to Dual"); } else { foreach (var GradleProjectName in GradleProjectNames) { Lines.Add($"pushd batch/{Shell.EscapeArgumentForShell(GradleProjectName.Split(':').First(), Shell.ShellArgumentStyle.Bash)}"); Lines.Add("./build.sh"); Lines.Add("popd"); } Lines.Add(@"echo To debug a APK, open Android Studio, select Profile or debug APK and select the generated APK, close Android Studio and copy the APK debug project under UserDirectory/ApkProjects to where the APK located\(ensure the overwrite of APK\), open the new project in Android Studio, add debug symbols for .so files, attach sources for Java files, select Run - Edit Configurations - Debugger - Debug type, and change its value to Dual"); } } Lines.Add(""); var BuildPath = BuildDirectory / "build.sh"; TextFile.WriteToFile(BuildPath, String.Join("\n", Lines), new System.Text.UTF8Encoding(false), !ForceRegenerate); if (Shell.Execute("chmod", "+x", BuildPath) != 0) { throw new InvalidOperationException("ErrorInExecution: chmod"); } } } }