/// <summary> /// Given a string of semi-colon delimited name=value pairs, this method parses it and creates /// a hash table containing the property names as keys and the property values as values. /// </summary> /// <param name="log"></param> /// <param name="propertyList"></param> /// <param name="propertiesTable"></param> /// <returns>true on success, false on failure.</returns> static internal bool GetTable(TaskLoggingHelper log, string parameterName, string[] propertyList, out Hashtable propertiesTable) { propertiesTable = null; if (propertyList != null) { propertiesTable = new Hashtable(StringComparer.OrdinalIgnoreCase); // Loop through the array. Each string in the array should be of the form: // MyPropName=MyPropValue foreach (string propertyNameValuePair in propertyList) { string propertyName = String.Empty; string propertyValue = String.Empty; // Find the first '=' sign in the string. int indexOfEqualsSign = propertyNameValuePair.IndexOf('='); // If we found one, then grab the stuff before it and put it into "propertyName", // and grab the stuff after it and put it into "propertyValue". But trim the // whitespace from beginning and end of both name and value. (When authoring a // project/targets file, people like to use whitespace and newlines to pretty up // the file format.) if (indexOfEqualsSign != -1) { propertyName = propertyNameValuePair.Substring(0, indexOfEqualsSign).Trim(); propertyValue = propertyNameValuePair.Substring(indexOfEqualsSign + 1).Trim(); } // Make sure we have a property name and property value (though the value is allowed to be blank). if (propertyName.Length == 0) { // No equals sign? No property name? That's no good to us. if (log != null) { log.LogErrorWithCodeFromResources("General.InvalidPropertyError", parameterName, propertyNameValuePair); } return false; } // Bag the property and its value. Trim whitespace from beginning and end of // both name and value. (When authoring a project/targets file, people like to // use whitespace and newlines to pretty up the file format.) propertiesTable[propertyName] = propertyValue; } } return true; }
internal static bool GetTable(TaskLoggingHelper log, string parameterName, string[] propertyList, out Dictionary <string, string> propertiesTable) { propertiesTable = null; if (propertyList != null) { propertiesTable = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); // Loop through the array. Each string in the array should be of the form: // MyPropName=MyPropValue foreach (string propertyNameValuePair in propertyList) { string propertyName = String.Empty; string propertyValue = String.Empty; // Find the first '=' sign in the string. int indexOfEqualsSign = propertyNameValuePair.IndexOf('='); // If we found one, then grab the stuff before it and put it into "propertyName", // and grab the stuff after it and put it into "propertyValue". But trim the // whitespace from beginning and end of both name and value. (When authoring a // project/targets file, people like to use whitespace and newlines to pretty up // the file format.) if (indexOfEqualsSign != -1) { propertyName = propertyNameValuePair.Substring(0, indexOfEqualsSign).Trim(); propertyValue = propertyNameValuePair.Substring(indexOfEqualsSign + 1).Trim(); } // Make sure we have a property name and property value (though the value is allowed to be blank). if (propertyName.Length == 0) { // No equals sign? No property name? That's no good to us. log?.LogErrorWithCodeFromResources("General.InvalidPropertyError", parameterName, propertyNameValuePair); return(false); } // Bag the property and its value. Trim whitespace from beginning and end of // both name and value. (When authoring a project/targets file, people like to // use whitespace and newlines to pretty up the file format.) propertiesTable[propertyName] = propertyValue; } } return(true); }
/// <summary> /// Create a taskfactory instance which contains the data that needs to be refreshed between task invocations /// </summary> public ITask CreateTask(IBuildEngine loggingHost) { // The assembly will have been compiled during class factory initialization, create an instance of it if (_compiledAssembly != null) { // In order to use the resource strings from the tasks assembly we need to register the resources with the task logging helper. var log = new TaskLoggingHelper(loggingHost, _nameOfTask) { TaskResources = AssemblyResources.PrimaryResources, HelpKeywordPrefix = "MSBuild." }; ITask taskInstance = Activator.CreateInstance(TaskType) as ITask; if (taskInstance == null) { log.LogErrorWithCodeFromResources("CodeTaskFactory.NeedsITaskInterface", _nameOfTask); } return(taskInstance); } return(null); }
/// <summary> /// Allows tool to handle the return code. /// This method will only be called with non-zero exitCode. If the non zero code is an acceptable one then we return true /// </summary> /// <returns>The return value of this method will be used as the task return value</returns> protected override bool HandleTaskExecutionErrors() { if (IsAcceptableReturnValue()) { return(true); } // We don't want to use ToolTask's implementation because it doesn't report the command line that failed. if (ExitCode == NativeMethods.SE_ERR_ACCESSDENIED) { _logPrivate.LogErrorWithCodeFromResources("Xaml.CommandFailedAccessDenied", this.CommandLine, ExitCode); } else { _logPrivate.LogErrorWithCodeFromResources("Xaml.CommandFailed", this.CommandLine, ExitCode); } return(false); }
protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) { if (ProvideCommandLineArgs) { CommandLineArgs = GetArguments(commandLineCommands, responseFileCommands) .Select(arg => new TaskItem(arg)).ToArray(); } if (SkipCompilerExecution) { return(0); } try { using var logger = new CompilerServerLogger(); string workingDir = CurrentDirectoryToUse(); string?tempDir = BuildServerConnection.GetTempPath(workingDir); if (!UseSharedCompilation || HasToolBeenOverridden || !BuildServerConnection.IsCompilerServerSupported) { LogCompilationMessage(logger, CompilationKind.Tool, $"using command line tool by design '{pathToTool}'"); return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands)); } _sharedCompileCts = new CancellationTokenSource(); logger.Log($"CommandLine = '{commandLineCommands}'"); logger.Log($"BuildResponseFile = '{responseFileCommands}'"); var clientDir = Path.GetDirectoryName(PathToManagedTool); if (clientDir is null || tempDir is null) { LogCompilationMessage(logger, CompilationKind.Tool, $"using command line tool because we could not find client directory '{PathToManagedTool}'"); return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands)); } var buildPaths = new BuildPathsAlt( clientDir: clientDir, workingDir: workingDir, // MSBuild doesn't need the .NET SDK directory sdkDir: null, tempDir: tempDir); // Note: using ToolArguments here (the property) since // commandLineCommands (the parameter) may have been mucked with // (to support using the dotnet cli) var responseTask = BuildServerConnection.RunServerCompilationAsync( Language, RoslynString.IsNullOrEmpty(SharedCompilationId) ? null : SharedCompilationId, GetArguments(ToolArguments, responseFileCommands).ToList(), buildPaths, keepAlive: null, libEnvVariable: LibDirectoryToUse(), logger: logger, cancellationToken: _sharedCompileCts.Token); responseTask.Wait(_sharedCompileCts.Token); ExitCode = HandleResponse(responseTask.Result, pathToTool, responseFileCommands, commandLineCommands, logger); } catch (OperationCanceledException) { ExitCode = 0; } catch (Exception e) { var util = new TaskLoggingHelper(this); util.LogErrorWithCodeFromResources("Compiler_UnexpectedException"); util.LogErrorFromException(e, showStackTrace: true, showDetail: true, file: null); ExitCode = -1; } finally { _sharedCompileCts?.Dispose(); _sharedCompileCts = null; } return(ExitCode); }
/// <summary> /// MSBuild engine will call this to initialize the factory. This should initialize the factory enough so that the factory can be asked /// whether or not task names can be created by the factory. /// </summary> public bool Initialize(string taskName, IDictionary <string, TaskPropertyInfo> taskParameters, string taskElementContents, IBuildEngine taskFactoryLoggingHost) { ErrorUtilities.VerifyThrowArgumentNull(taskName, nameof(taskName)); ErrorUtilities.VerifyThrowArgumentNull(taskParameters, nameof(taskParameters)); var log = new TaskLoggingHelper(taskFactoryLoggingHost, taskName) { TaskResources = AssemblyResources.PrimaryResources, HelpKeywordPrefix = "MSBuild." }; if (taskElementContents == null) { log.LogErrorWithCodeFromResources("Xaml.MissingTaskBody"); return(false); } TaskElementContents = taskElementContents.Trim(); // Attempt to load the task TaskParser parser = new TaskParser(); bool parseSuccessful = parser.Parse(TaskElementContents, taskName); TaskName = parser.GeneratedTaskName; TaskNamespace = parser.Namespace; var generator = new TaskGenerator(parser); CodeCompileUnit dom = generator.GenerateCode(); string pathToMSBuildBinaries = ToolLocationHelper.GetPathToBuildTools(ToolLocationHelper.CurrentToolsVersion); // create the code generator options // Since we are running msbuild 12.0 these had better load. var compilerParameters = new CompilerParameters ( new[] { "System.dll", Path.Combine(pathToMSBuildBinaries, "Microsoft.Build.Framework.dll"), Path.Combine(pathToMSBuildBinaries, "Microsoft.Build.Utilities.Core.dll"), Path.Combine(pathToMSBuildBinaries, "Microsoft.Build.Tasks.Core.dll") } ) { GenerateInMemory = true, TreatWarningsAsErrors = false }; // create the code provider var codegenerator = CodeDomProvider.CreateProvider("cs"); CompilerResults results; bool debugXamlTask = Environment.GetEnvironmentVariable("MSBUILDWRITEXAMLTASK") == "1"; if (debugXamlTask) { using (var outputWriter = new StreamWriter(taskName + "_XamlTask.cs")) { var options = new CodeGeneratorOptions { BlankLinesBetweenMembers = true, BracingStyle = "C" }; codegenerator.GenerateCodeFromCompileUnit(dom, outputWriter, options); } results = codegenerator.CompileAssemblyFromFile(compilerParameters, taskName + "_XamlTask.cs"); } else { results = codegenerator.CompileAssemblyFromDom(compilerParameters, dom); } try { _taskAssembly = results.CompiledAssembly; } catch (FileNotFoundException) { // This occurs if there is a failure to compile the assembly. We just pass through because we will take care of the failure below. } if (_taskAssembly == null) { var errorList = new StringBuilder(); errorList.AppendLine(); foreach (CompilerError error in results.Errors) { if (error.IsWarning) { continue; } if (debugXamlTask) { errorList.AppendFormat(Thread.CurrentThread.CurrentUICulture, "({0},{1}) {2}", error.Line, error.Column, error.ErrorText).AppendLine(); } else { errorList.AppendLine(error.ErrorText); } } log.LogErrorWithCodeFromResources("Xaml.TaskCreationFailed", errorList.ToString()); } return(!log.HasLoggedErrors); }
public bool Initialize(string taskName, IDictionary <string, TaskPropertyInfo> taskParameters, string taskElementContents, IBuildEngine taskFactoryLoggingHost) { CompilerResults results; Microsoft.Build.Shared.ErrorUtilities.VerifyThrowArgumentNull(taskName, "taskName"); Microsoft.Build.Shared.ErrorUtilities.VerifyThrowArgumentNull(taskParameters, "taskParameters"); TaskLoggingHelper helper = new TaskLoggingHelper(taskFactoryLoggingHost, taskName) { TaskResources = Microsoft.Build.Shared.AssemblyResources.PrimaryResources, HelpKeywordPrefix = "MSBuild." }; if (taskElementContents == null) { helper.LogErrorWithCodeFromResources("Xaml.MissingTaskBody", new object[0]); return(false); } this.TaskElementContents = taskElementContents.Trim(); TaskParser parser = new TaskParser(); parser.Parse(this.TaskElementContents, taskName); this.TaskName = parser.GeneratedTaskName; this.TaskNamespace = parser.Namespace; CodeCompileUnit compileUnit = new TaskGenerator(parser).GenerateCode(); Assembly assembly = Assembly.LoadWithPartialName("System"); Assembly assembly2 = Assembly.LoadWithPartialName("Microsoft.Build.Framework"); Assembly assembly3 = Assembly.LoadWithPartialName("Microsoft.Build.Utilities.V4.0"); Assembly assembly4 = Assembly.LoadWithPartialName("Microsoft.Build.Tasks.V4.0"); CompilerParameters parameters = new CompilerParameters(new string[] { assembly.Location, assembly2.Location, assembly3.Location, assembly4.Location }) { GenerateInMemory = true, TreatWarningsAsErrors = false }; CodeDomProvider provider = CodeDomProvider.CreateProvider("cs"); bool flag = Environment.GetEnvironmentVariable("MSBUILDWRITEXAMLTASK") == "1"; if (flag) { using (StreamWriter writer = new StreamWriter(taskName + "_XamlTask.cs")) { CodeGeneratorOptions options = new CodeGeneratorOptions { BlankLinesBetweenMembers = true, BracingStyle = "C" }; provider.GenerateCodeFromCompileUnit(compileUnit, writer, options); } results = provider.CompileAssemblyFromFile(parameters, new string[] { taskName + "_XamlTask.cs" }); } else { results = provider.CompileAssemblyFromDom(parameters, new CodeCompileUnit[] { compileUnit }); } try { this.taskAssembly = results.CompiledAssembly; } catch (FileNotFoundException) { } if (this.taskAssembly == null) { StringBuilder builder = new StringBuilder(); builder.AppendLine(); foreach (CompilerError error in results.Errors) { if (!error.IsWarning) { if (flag) { builder.AppendLine(string.Format(Thread.CurrentThread.CurrentUICulture, "({0},{1}) {2}", new object[] { error.Line, error.Column, error.ErrorText })); } else { builder.AppendLine(error.ErrorText); } } } helper.LogErrorWithCodeFromResources("Xaml.TaskCreationFailed", new object[] { builder.ToString() }); } return(!helper.HasLoggedErrors); }
/// <summary> /// This method will take a sdkToolsPath and a toolName and return the path to the tool if it is found and exists. /// /// First the method will try and find the tool under the sdkToolsPath taking into account the current processor architecture /// If the tool could not be found the method will try and find the tool under the sdkToolsPath (which should point to the x86 sdk directory). /// /// Finally if the method has not found the tool yet it will fallback and use the toolslocation helper method to try and find the tool. /// </summary> /// <returns>Path including the toolName of the tool if found, null if it is not found</returns> internal static string GeneratePathToTool(FileExists fileExists, string currentArchitecture, string sdkToolsPath, string toolName, TaskLoggingHelper log, bool logErrorsAndWarnings) { // Null until we combine the toolname with the path. string pathToTool = null; if (!String.IsNullOrEmpty(sdkToolsPath)) { string processorSpecificToolDirectory = String.Empty; try { switch (currentArchitecture) { // There may not be an arm directory so we will fall back to the x86 tool location // but if there is then we should try and use it. case ProcessorArchitecture.ARM: processorSpecificToolDirectory = Path.Combine(sdkToolsPath, "arm"); break; case ProcessorArchitecture.AMD64: processorSpecificToolDirectory = Path.Combine(sdkToolsPath, "x64"); break; case ProcessorArchitecture.IA64: processorSpecificToolDirectory = Path.Combine(sdkToolsPath, "ia64"); break; case ProcessorArchitecture.X86: default: processorSpecificToolDirectory = sdkToolsPath; break; } pathToTool = Path.Combine(processorSpecificToolDirectory, toolName); if (!fileExists(pathToTool)) { // Try falling back to the x86 location if (currentArchitecture != ProcessorArchitecture.X86) { pathToTool = Path.Combine(sdkToolsPath, toolName); } } else { return(pathToTool); } } catch (ArgumentException e) { // Catch exceptions from path.combine log.LogErrorWithCodeFromResources("General.SdkToolsPathError", toolName, e.Message); return(null); } if (fileExists(pathToTool)) { return(pathToTool); } else { if (logErrorsAndWarnings) { // Log an error indicating we could not find it in the processor specific architecture or x86 locations. // We could not find the tool at all, lot a error. log.LogWarningWithCodeFromResources("General.PlatformSDKFileNotFoundSdkToolsPath", toolName, processorSpecificToolDirectory, sdkToolsPath); } } } else { if (logErrorsAndWarnings) { log.LogMessageFromResources(MessageImportance.Low, "General.SdkToolsPathNotSpecifiedOrToolDoesNotExist", toolName, sdkToolsPath); } } // Fall back and see if we can find it with the toolsLocation helper methods. This is not optimal because // the location they are looking at is based on when the Microsoft.Build.Utilities.dll was compiled // but it is better than nothing. if (null == pathToTool || !fileExists(pathToTool)) { pathToTool = FindSDKToolUsingToolsLocationHelper(toolName); if (pathToTool == null && logErrorsAndWarnings) { log.LogErrorWithCodeFromResources("General.SdkToolsPathToolDoesNotExist", toolName, sdkToolsPath, ToolLocationHelper.GetDotNetFrameworkSdkRootRegistryKey(TargetDotNetFrameworkVersion.VersionLatest, VisualStudioVersion.VersionLatest)); } } return(pathToTool); }
internal static string GeneratePathToTool(Microsoft.Build.Shared.FileExists fileExists, string currentArchitecture, string sdkToolsPath, string toolName, TaskLoggingHelper log, bool logErrorsAndWarnings) { string path = null; if (!string.IsNullOrEmpty(sdkToolsPath)) { string str2 = string.Empty; try { string str4 = currentArchitecture; if (str4 == null) { goto Label_0061; } if (!(str4 == "AMD64")) { if (str4 == "IA64") { goto Label_0053; } if (str4 == "x86") { } goto Label_0061; } str2 = Path.Combine(sdkToolsPath, "x64"); goto Label_0063; Label_0053: str2 = Path.Combine(sdkToolsPath, "ia64"); goto Label_0063; Label_0061: str2 = sdkToolsPath; Label_0063: path = Path.Combine(str2, toolName); if (!fileExists(path)) { if (currentArchitecture != "x86") { path = Path.Combine(sdkToolsPath, toolName); } } else { return(path); } } catch (ArgumentException exception) { log.LogErrorWithCodeFromResources("General.SdkToolsPathError", new object[] { toolName, exception.Message }); return(null); } if (fileExists(path)) { return(path); } if (logErrorsAndWarnings) { log.LogWarningWithCodeFromResources("General.PlatformSDKFileNotFoundSdkToolsPath", new object[] { toolName, str2, sdkToolsPath }); } } else if (logErrorsAndWarnings) { log.LogMessageFromResources(MessageImportance.Low, "General.SdkToolsPathNotSpecifiedOrToolDoesNotExist", new object[] { toolName, sdkToolsPath }); } if ((path == null) || !fileExists(path)) { path = FindSDKToolUsingToolsLocationHelper(toolName); if ((path == null) && logErrorsAndWarnings) { log.LogErrorWithCodeFromResources("General.SdkToolsPathToolDoesNotExist", new object[] { toolName, sdkToolsPath, ToolLocationHelper.GetDotNetFrameworkSdkRootRegistryKey(TargetDotNetFrameworkVersion.Version40) }); } } return(path); }
/// <summary> /// Given a string of semi-colon delimited name=value pairs, this method parses it and creates /// a hash table containing the property names as keys and the property values as values. /// This method escapes any special characters found in the property values, in case they /// are going to be passed to a method (such as that expects the appropriate escaping to have happened /// already. /// </summary> /// <param name="log"></param> /// <param name="propertyList"></param> /// <param name="propertiesTable"></param> /// <returns>true on success, false on failure.</returns> static internal bool GetTableWithEscaping(TaskLoggingHelper log, string parameterName, string syntaxName, string[] propertyNameValueStrings, out Hashtable finalPropertiesTable) { finalPropertiesTable = null; if (propertyNameValueStrings != null) { finalPropertiesTable = new Hashtable(StringComparer.OrdinalIgnoreCase); List<PropertyNameValuePair> finalPropertiesList = new List<PropertyNameValuePair>(); // Loop through the array. Each string in the array should be of the form: // MyPropName=MyPropValue foreach (string propertyNameValueString in propertyNameValueStrings) { // Find the first '=' sign in the string. int indexOfEqualsSign = propertyNameValueString.IndexOf('='); if (indexOfEqualsSign != -1) { // If we found one, then grab the stuff before it and put it into "propertyName", // and grab the stuff after it and put it into "propertyValue". But trim the // whitespace from beginning and end of both name and value. (When authoring a // project/targets file, people like to use whitespace and newlines to pretty up // the file format.) string propertyName = propertyNameValueString.Substring(0, indexOfEqualsSign).Trim(); string propertyValue = EscapingUtilities.Escape(propertyNameValueString.Substring(indexOfEqualsSign + 1).Trim()); // Make sure we have a property name and property value (though the value is allowed to be blank). if (propertyName.Length == 0) { // No property name? That's no good to us. if (log != null) { log.LogErrorWithCodeFromResources("General.InvalidPropertyError", syntaxName, propertyNameValueString); } return false; } // Store the property in our list. finalPropertiesList.Add(new PropertyNameValuePair(propertyName, propertyValue)); } else { // There's no '=' sign in the string. When this happens, we treat this string as basically // an appendage on the value of the previous property. For example, if the project file contains // // <PropertyGroup> // <WarningsAsErrors>1234;5678;9999</WarningsAsErrors> // </PropertyGroup> // <Target Name="Build"> // <MSBuild Projects="ConsoleApplication1.csproj" // Properties="WarningsAsErrors=$(WarningsAsErrors)"/> // </Target> // // , then this method (GetTableWithEscaping) will see this: // // propertyNameValueStrings[0] = "WarningsAsErrors=1234" // propertyNameValueStrings[1] = "5678" // propertyNameValueStrings[2] = "9999" // // And what we actually want to end up with in our final hashtable is this: // // NAME VALUE // =================== ================================ // WarningsAsErrors 1234;5678;9999 // if (finalPropertiesList.Count > 0) { // There was a property definition previous to this one. Append the current string // to that previous value, using semicolon as a separator. string propertyValue = EscapingUtilities.Escape(propertyNameValueString.Trim()); finalPropertiesList[finalPropertiesList.Count - 1].Value.Append(';'); finalPropertiesList[finalPropertiesList.Count - 1].Value.Append(propertyValue); } else { // No equals sign in the very first property? That's a problem. if (log != null) { log.LogErrorWithCodeFromResources("General.InvalidPropertyError", syntaxName, propertyNameValueString); } return false; } } } // Convert the data in the List to a Hashtable, because that's what the MSBuild task eventually // needs to pass onto the engine. if (log != null) { log.LogMessageFromText(parameterName, MessageImportance.Low); } foreach (PropertyNameValuePair propertyNameValuePair in finalPropertiesList) { string propertyValue = OpportunisticIntern.StringBuilderToString(propertyNameValuePair.Value); finalPropertiesTable[propertyNameValuePair.Name] = propertyValue; if (log != null) { log.LogMessageFromText(String.Format(CultureInfo.InvariantCulture, " {0}={1}", propertyNameValuePair.Name, propertyValue), MessageImportance.Low); } } } return true; }
internal static bool GetTableWithEscaping(TaskLoggingHelper log, string parameterName, string syntaxName, string[] propertyNameValueStrings, out Dictionary <string, string> finalPropertiesTable) { finalPropertiesTable = null; if (propertyNameValueStrings != null) { finalPropertiesTable = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var finalPropertiesList = new List <PropertyNameValuePair>(); // Loop through the array. Each string in the array should be of the form: // MyPropName=MyPropValue foreach (string propertyNameValueString in propertyNameValueStrings) { // Find the first '=' sign in the string. int indexOfEqualsSign = propertyNameValueString.IndexOf('='); if (indexOfEqualsSign != -1) { // If we found one, then grab the stuff before it and put it into "propertyName", // and grab the stuff after it and put it into "propertyValue". But trim the // whitespace from beginning and end of both name and value. (When authoring a // project/targets file, people like to use whitespace and newlines to pretty up // the file format.) string propertyName = propertyNameValueString.Substring(0, indexOfEqualsSign).Trim(); string propertyValue = EscapingUtilities.Escape(propertyNameValueString.Substring(indexOfEqualsSign + 1).Trim()); // Make sure we have a property name and property value (though the value is allowed to be blank). if (propertyName.Length == 0) { // No property name? That's no good to us. log?.LogErrorWithCodeFromResources("General.InvalidPropertyError", syntaxName, propertyNameValueString); return(false); } // Store the property in our list. finalPropertiesList.Add(new PropertyNameValuePair(propertyName, propertyValue)); } else { // There's no '=' sign in the string. When this happens, we treat this string as basically // an appendage on the value of the previous property. For example, if the project file contains // // <PropertyGroup> // <WarningsAsErrors>1234;5678;9999</WarningsAsErrors> // </PropertyGroup> // <Target Name="Build"> // <MSBuild Projects="ConsoleApplication1.csproj" // Properties="WarningsAsErrors=$(WarningsAsErrors)"/> // </Target> // // , then this method (GetTableWithEscaping) will see this: // // propertyNameValueStrings[0] = "WarningsAsErrors=1234" // propertyNameValueStrings[1] = "5678" // propertyNameValueStrings[2] = "9999" // // And what we actually want to end up with in our final hashtable is this: // // NAME VALUE // =================== ================================ // WarningsAsErrors 1234;5678;9999 // if (finalPropertiesList.Count > 0) { // There was a property definition previous to this one. Append the current string // to that previous value, using semicolon as a separator. string propertyValue = EscapingUtilities.Escape(propertyNameValueString.Trim()); finalPropertiesList[finalPropertiesList.Count - 1].Value.Add(propertyValue); } else { // No equals sign in the very first property? That's a problem. log?.LogErrorWithCodeFromResources("General.InvalidPropertyError", syntaxName, propertyNameValueString); return(false); } } } // Convert the data in the List to a Hashtable, because that's what the MSBuild task eventually // needs to pass onto the engine. log?.LogMessageFromText(parameterName, MessageImportance.Low); using SpanBasedStringBuilder stringBuilder = Strings.GetSpanBasedStringBuilder(); foreach (PropertyNameValuePair propertyNameValuePair in finalPropertiesList) { stringBuilder.Clear(); bool needsSemicolon = false; foreach (string valueFragment in propertyNameValuePair.Value) { if (needsSemicolon) { stringBuilder.Append(";"); } needsSemicolon = true; stringBuilder.Append(valueFragment); } string propertyValue = stringBuilder.ToString(); finalPropertiesTable[propertyNameValuePair.Name] = propertyValue; log?.LogMessageFromText( $" {propertyNameValuePair.Name}={propertyValue}", MessageImportance.Low); } } return(true); }
/// <summary> /// Attempts to resolve assembly references that were specified by the user. /// </summary> /// <param name="log">A <see cref="TaskLoggingHelper"/> used for logging.</param> /// <param name="taskInfo">A <see cref="TaskInfo"/> object containing details about the task.</param> /// <param name="items">Receives the list of full paths to resolved assemblies.</param> /// <returns><code>true</code> if all assemblies could be resolved, otherwise <code>false</code>.</returns> /// <remarks>The user can specify a short name like My.Assembly or My.Assembly.dll. In this case we'll /// attempt to look it up in the directory containing our reference assemblies. They can also specify a /// full path and we'll do no resolution. At this time, these are the only two resolution mechanisms. /// Perhaps in the future this could be more powerful by using NuGet to resolve assemblies but we think /// that is too complicated for a simple in-line task. If users have more complex requirements, they /// can compile their own task library.</remarks> internal static bool TryResolveAssemblyReferences(TaskLoggingHelper log, TaskInfo taskInfo, out ITaskItem[] items) { // Store the list of resolved assemblies because a user can specify a short name or a full path // ISet <string> resolvedAssemblyReferences = new HashSet <string>(StringComparer.OrdinalIgnoreCase); // Keeps track if there were one or more unresolved assemblies // bool hasInvalidReference = false; // Start with the user specified references and include all of the default references that are language agnostic // IEnumerable <string> references = taskInfo.References.Union(DefaultReferences[String.Empty]); if (DefaultReferences.ContainsKey(taskInfo.CodeLanguage)) { // Append default references for the specific language // references = references.Union(DefaultReferences[taskInfo.CodeLanguage]); } // Loop through the user specified references as well as the default references // foreach (string reference in references) { // The user specified a full path to an assembly, so there is no need to resolve // if (File.Exists(reference)) { // The path could be relative like ..\Assembly.dll so we need to get the full path // resolvedAssemblyReferences.Add(Path.GetFullPath(reference)); continue; } // Attempt to "resolve" the assembly by getting a full path to our distributed reference assemblies // string assemblyFileName = reference.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || reference.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ? reference : $"{reference}.dll"; string possiblePath = Path.Combine(ThisAssemblyParentDirectoryLazy.Value, ReferenceAssemblyDirectoryName, assemblyFileName); if (File.Exists(possiblePath)) { resolvedAssemblyReferences.Add(possiblePath); continue; } // Could not resolve the assembly. We currently don't support looking things up the GAC so that in-line task // assemblies are portable across platforms // log.LogErrorWithCodeFromResources("CodeTaskFactory_CouldNotFindReferenceAssembly", reference); hasInvalidReference = true; } // Transform the list of resolved assemblies to TaskItems if they were all resolved // items = hasInvalidReference ? null : resolvedAssemblyReferences.Select(i => (ITaskItem) new TaskItem(i)).ToArray(); return(!hasInvalidReference); }
/// <summary> /// Parses and validates the body of the <UsingTask />. /// </summary> /// <param name="log">A <see cref="TaskLoggingHelper"/> used to log events during parsing.</param> /// <param name="taskName">The name of the task.</param> /// <param name="taskBody">The raw inner XML string of the <UsingTask />> to parse and validate.</param> /// <param name="parameters">An <see cref="ICollection{TaskPropertyInfo}"/> containing parameters for the task.</param> /// <param name="taskInfo">A <see cref="TaskInfo"/> object that receives the details of the parsed task.</param> /// <returns><code>true</code> if the task body was successfully parsed, otherwise <code>false</code>.</returns> /// <remarks> /// The <paramref name="taskBody"/> will look like this: /// <![CDATA[ /// /// <Using Namespace="Namespace" /> /// <Reference Include="AssemblyName|AssemblyPath" /> /// <Code Type="Fragment|Method|Class" Language="cs|vb" Source="Path"> /// // Source code /// </Code> /// /// ]]> /// </remarks> internal static bool TryLoadTaskBody(TaskLoggingHelper log, string taskName, string taskBody, ICollection <TaskPropertyInfo> parameters, out TaskInfo taskInfo) { taskInfo = new TaskInfo() { CodeLanguage = "CS", CodeType = CodeTaskFactoryCodeType.Fragment, Name = taskName, }; XDocument document; try { // For legacy reasons, the inner XML of the <UsingTask /> has no document element. So we have to add a top-level // element around it so it can be parsed. // document = XDocument.Parse($"<Task>{taskBody}</Task>"); } catch (Exception e) { log.LogErrorWithCodeFromResources("CodeTaskFactory_InvalidTaskXml", e.Message); return(false); } if (document.Root == null) { log.LogErrorWithCodeFromResources("CodeTaskFactory_InvalidTaskXml"); return(false); } XElement codeElement = null; // Loop through the children, ignoring ones we don't care about, parsing valid ones, and logging an error if we // encounter any element that is not recognized. // foreach (XNode node in document.Root.Nodes() .Where(i => i.NodeType != XmlNodeType.Comment && i.NodeType != XmlNodeType.Whitespace)) { switch (node.NodeType) { case XmlNodeType.Element: XElement child = (XElement)node; // Parse known elements and go to the default case if its an unknown element // if (child.Name.LocalName.Equals("Code", StringComparison.OrdinalIgnoreCase)) { if (codeElement != null) { // Only one <Code /> element is allowed. // log.LogErrorWithCodeFromResources("CodeTaskFactory_MultipleCodeNodes"); return(false); } codeElement = child; } else if (child.Name.LocalName.Equals("Reference", StringComparison.OrdinalIgnoreCase)) { XAttribute includeAttribute = child.Attributes().FirstOrDefault(i => i.Name.LocalName.Equals("Include", StringComparison.OrdinalIgnoreCase)); if (String.IsNullOrWhiteSpace(includeAttribute?.Value)) { // A <Reference Include="" /> is not allowed. // log.LogErrorWithCodeFromResources("CodeTaskFactory_AttributeEmpty", "Include", "Reference"); return(false); } // Store the reference in the list // taskInfo.References.Add(includeAttribute.Value.Trim()); } else if (child.Name.LocalName.Equals("Using", StringComparison.OrdinalIgnoreCase)) { XAttribute namespaceAttribute = child.Attributes().FirstOrDefault(i => i.Name.LocalName.Equals("Namespace", StringComparison.OrdinalIgnoreCase)); if (String.IsNullOrWhiteSpace(namespaceAttribute?.Value)) { // A <Using Namespace="" /> is not allowed // log.LogErrorWithCodeFromResources("CodeTaskFactory_AttributeEmpty", "Namespace", "Using"); return(false); } // Store the using in the list // taskInfo.Namespaces.Add(namespaceAttribute.Value.Trim()); } else { log.LogErrorWithCodeFromResources("CodeTaskFactory_InvalidElementLocation", child.Name.LocalName, document.Root.Name.LocalName, " Valid child elements are <Code>, <Reference>, and <Using>."); return(false); } break; default: log.LogErrorWithCodeFromResources("CodeTaskFactory_InvalidElementLocation", node.NodeType, document.Root.Name.LocalName, " Valid child elements are <Code>, <Reference>, and <Using>."); return(false); } } if (codeElement == null) { // <Code /> element is required so if we didn't find it then we need to error // log.LogErrorWithCodeFromResources("CodeTaskFactory_CodeElementIsMissing", taskName); return(false); } // Copies the source code from the inner text of the <Code /> element. This might be override later if the user specified // a file instead. // taskInfo.SourceCode = codeElement.Value; // Parse the attributes of the <Code /> element // XAttribute languageAttribute = codeElement.Attributes().FirstOrDefault(i => i.Name.LocalName.Equals("Language", StringComparison.OrdinalIgnoreCase)); XAttribute sourceAttribute = codeElement.Attributes().FirstOrDefault(i => i.Name.LocalName.Equals("Source", StringComparison.OrdinalIgnoreCase)); XAttribute typeAttribute = codeElement.Attributes().FirstOrDefault(i => i.Name.LocalName.Equals("Type", StringComparison.OrdinalIgnoreCase)); if (sourceAttribute != null) { if (String.IsNullOrWhiteSpace(sourceAttribute.Value)) { // A <Code Source="" /> is not allowed // log.LogErrorWithCodeFromResources("CodeTaskFactory_AttributeEmpty", "Source", "Code"); return(false); } // Instead of using the inner text of the <Code /> element, read the specified file as source code // taskInfo.CodeType = CodeTaskFactoryCodeType.Class; taskInfo.SourceCode = File.ReadAllText(sourceAttribute.Value.Trim()); } else if (typeAttribute != null) { if (String.IsNullOrWhiteSpace(typeAttribute.Value)) { // A <Code Type="" /> is not allowed // log.LogErrorWithCodeFromResources("CodeTaskFactory_AttributeEmpty", "Type", "Code"); return(false); } // Attempt to parse the code type as a CodeTaskFactoryCodeType // if (!Enum.TryParse(typeAttribute.Value.Trim(), ignoreCase: true, result: out CodeTaskFactoryCodeType codeType)) { log.LogErrorWithCodeFromResources("CodeTaskFactory_InvalidCodeType", typeAttribute.Value, String.Join(", ", Enum.GetNames(typeof(CodeTaskFactoryCodeType)))); return(false); } taskInfo.CodeType = codeType; } // Warn that <ParameterGroup/> is ignored if any parameters are supplied when Type="Class". // if (taskInfo.CodeType == CodeTaskFactoryCodeType.Class && parameters.Any()) { log.LogWarningWithCodeFromResources("CodeTaskFactory_ParameterGroupIgnoredForCodeTypeClass"); } if (languageAttribute != null) { if (String.IsNullOrWhiteSpace(languageAttribute.Value)) { // A <Code Language="" /> is not allowed // log.LogErrorWithCodeFromResources("CodeTaskFactory_AttributeEmpty", "Language", "Code"); return(false); } if (ValidCodeLanguages.ContainsKey(languageAttribute.Value)) { // The user specified one of the primary code languages using our vernacular // taskInfo.CodeLanguage = languageAttribute.Value.ToUpperInvariant(); } else { bool foundValidCodeLanguage = false; // Attempt to map the user specified value as an alias to our vernacular for code languages // foreach (string validLanguage in ValidCodeLanguages.Keys) { if (ValidCodeLanguages[validLanguage].Contains(languageAttribute.Value)) { taskInfo.CodeLanguage = validLanguage; foundValidCodeLanguage = true; break; } } if (!foundValidCodeLanguage) { // The user specified a code language we don't support // log.LogErrorWithCodeFromResources("CodeTaskFactory_InvalidCodeLanguage", languageAttribute.Value, String.Join(", ", ValidCodeLanguages.Keys)); return(false); } } } if (String.IsNullOrWhiteSpace(taskInfo.SourceCode)) { // The user did not specify a path to source code or source code within the <Code /> element. // log.LogErrorWithCodeFromResources("CodeTaskFactory_NoSourceCode"); return(false); } ApplySourceCodeTemplate(taskInfo, parameters); return(true); }
internal bool TryResolveAssemblyReferences(TaskLoggingHelper log, RoslynCodeTaskFactoryTaskInfo taskInfo, out ITaskItem[] items) { // Store the list of resolved assemblies because a user can specify a short name or a full path ISet <string> resolvedAssemblyReferences = new HashSet <string>(StringComparer.OrdinalIgnoreCase); // Keeps track if there were one or more unresolved assemblies bool hasInvalidReference = false; // Start with the user specified references and include all of the default references that are language agnostic IEnumerable <string> references = taskInfo.References.Union(DefaultReferences[String.Empty]); if (DefaultReferences.ContainsKey(taskInfo.CodeLanguage)) { // Append default references for the specific language references = references.Union(DefaultReferences[taskInfo.CodeLanguage]); } List <string> directoriesToAddToAppDomain = new(); // Loop through the user specified references as well as the default references foreach (string reference in references) { // The user specified a full path to an assembly, so there is no need to resolve if (FileSystems.Default.FileExists(reference)) { // The path could be relative like ..\Assembly.dll so we need to get the full path string fullPath = Path.GetFullPath(reference); directoriesToAddToAppDomain.Add(Path.GetDirectoryName(fullPath)); resolvedAssemblyReferences.Add(fullPath); continue; } // Attempt to "resolve" the assembly by getting a full path to our distributed reference assemblies string assemblyFileName = reference.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || reference.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ? reference : $"{reference}.dll"; string resolvedDir = new[] { Path.Combine(ThisAssemblyDirectoryLazy.Value, ReferenceAssemblyDirectoryName), ThisAssemblyDirectoryLazy.Value, } .Concat(MonoLibDirs) .FirstOrDefault(p => File.Exists(Path.Combine(p, assemblyFileName))); if (resolvedDir != null) { resolvedAssemblyReferences.Add(Path.Combine(resolvedDir, assemblyFileName)); continue; } // Could not resolve the assembly. We currently don't support looking things up the GAC so that in-line task // assemblies are portable across platforms log.LogErrorWithCodeFromResources("CodeTaskFactory.CouldNotFindReferenceAssembly", reference); hasInvalidReference = true; } // Transform the list of resolved assemblies to TaskItems if they were all resolved items = hasInvalidReference ? null : resolvedAssemblyReferences.Select(i => (ITaskItem) new TaskItem(i)).ToArray(); handlerAddedToAppDomain = (_, eventArgs) => TryLoadAssembly(directoriesToAddToAppDomain, new AssemblyName(eventArgs.Name)); AppDomain.CurrentDomain.AssemblyResolve += handlerAddedToAppDomain; return(!hasInvalidReference);
/// <summary> /// Initialze the task factory /// </summary> public bool Initialize(string taskName, IDictionary <string, TaskPropertyInfo> taskParameters, string taskElementContents, IBuildEngine taskFactoryLoggingHost) { _nameOfTask = taskName; _log = new TaskLoggingHelper(taskFactoryLoggingHost, taskName) { TaskResources = AssemblyResources.PrimaryResources, HelpKeywordPrefix = "MSBuild." }; XmlNode taskContent = ExtractTaskContent(taskElementContents); if (taskContent == null) { // Just return false because we have already logged the error in ExtractTaskContents return(false); } bool validatedTaskNode = ValidateTaskNode(); if (!validatedTaskNode) { return(false); } if (taskContent.Attributes["Type"] != null) { _type = taskContent.Attributes["Type"].Value; if (_type.Length == 0) { _log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmpty", "Type"); return(false); } } if (taskContent.Attributes["Language"] != null) { _language = taskContent.Attributes["Language"].Value; if (_language.Length == 0) { _log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmpty", "Language"); return(false); } } if (taskContent.Attributes["Source"] != null) { _sourcePath = taskContent.Attributes["Source"].Value; if (_sourcePath.Length == 0) { _log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmpty", "Source"); return(false); } if (_type == null) { _type = "Class"; } } _referencedAssemblies = ExtractReferencedAssemblies(); if (_log.HasLoggedErrors) { return(false); } _usingNamespaces = ExtractUsingNamespaces(); if (_log.HasLoggedErrors) { return(false); } _sourceCode = taskContent.InnerText; if (_type == null) { _type = "Fragment"; } if (_language == null) { _language = "cs"; } if (String.Equals(_type, "Fragment", StringComparison.OrdinalIgnoreCase)) { _typeIsFragment = true; _typeIsMethod = false; } else if (String.Equals(_type, "Method", StringComparison.OrdinalIgnoreCase)) { _typeIsFragment = false; _typeIsMethod = true; } _taskParameterTypeInfo = taskParameters; _compiledAssembly = CompileInMemoryAssembly(); // If it wasn't compiled, it logged why. // If it was, continue. if (_compiledAssembly != null) { // Now go find the type int he compiled assembly. Type[] exportedTypes = _compiledAssembly.GetExportedTypes(); Type fullNameMatch = null; Type partialNameMatch = null; foreach (Type exportedType in exportedTypes) { string exportedTypeName = exportedType.FullName; if (exportedTypeName.Equals(_nameOfTask, StringComparison.OrdinalIgnoreCase)) { fullNameMatch = exportedType; break; } else if (partialNameMatch == null && exportedTypeName.EndsWith(_nameOfTask, StringComparison.OrdinalIgnoreCase)) { partialNameMatch = exportedType; } } TaskType = fullNameMatch ?? partialNameMatch; if (TaskType == null) { _log.LogErrorWithCodeFromResources("CodeTaskFactory.CouldNotFindTaskInAssembly", _nameOfTask); } } return(!_log.HasLoggedErrors); }
internal static bool TryLoadTaskBody(TaskLoggingHelper log, string taskName, string taskBody, ICollection <TaskPropertyInfo> parameters, out RoslynCodeTaskFactoryTaskInfo taskInfo) { taskInfo = new RoslynCodeTaskFactoryTaskInfo { CodeLanguage = "CS", CodeType = RoslynCodeTaskFactoryCodeType.Fragment, Name = taskName, }; XDocument document; try { // For legacy reasons, the inner XML of the <UsingTask /> has no document element. So we have to add a top-level // element around it so it can be parsed. document = XDocument.Parse($"<Task>{taskBody}</Task>"); } catch (Exception e) { log.LogErrorWithCodeFromResources("CodeTaskFactory.InvalidTaskXml", e.Message); return(false); } if (document.Root == null) { log.LogErrorWithCodeFromResources("CodeTaskFactory.InvalidTaskXml", String.Empty); return(false); } XElement codeElement = null; // Loop through the children, ignoring ones we don't care about, parsing valid ones, and logging an error if we // encounter any element that is not recognized. foreach (XNode node in document.Root.Nodes() .Where(i => i.NodeType != XmlNodeType.Comment && i.NodeType != XmlNodeType.Whitespace)) { switch (node.NodeType) { case XmlNodeType.Element: XElement child = (XElement)node; // Parse known elements and go to the default case if its an unknown element if (child.Name.LocalName.Equals("Code")) { if (codeElement != null) { // Only one <Code /> element is allowed. log.LogErrorWithCodeFromResources("CodeTaskFactory.MultipleCodeNodes"); return(false); } codeElement = child; } else if (child.Name.LocalName.Equals("Reference")) { XAttribute includeAttribute = child.Attributes().FirstOrDefault(i => i.Name.LocalName.Equals("Include")); if (String.IsNullOrWhiteSpace(includeAttribute?.Value)) { // A <Reference Include="" /> is not allowed. log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmptyWithElement", "Include", "Reference"); return(false); } // Store the reference in the list taskInfo.References.Add(includeAttribute.Value.Trim()); } else if (child.Name.LocalName.Equals("Using")) { XAttribute namespaceAttribute = child.Attributes().FirstOrDefault(i => i.Name.LocalName.Equals("Namespace")); if (String.IsNullOrWhiteSpace(namespaceAttribute?.Value)) { // A <Using Namespace="" /> is not allowed log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmptyWithElement", "Namespace", "Using"); return(false); } // Store the using in the list taskInfo.Namespaces.Add(namespaceAttribute.Value.Trim()); } else { log.LogErrorWithCodeFromResources("CodeTaskFactory.InvalidElementLocation", child.Name.LocalName, document.Root.Name.LocalName); return(false); } break; default: log.LogErrorWithCodeFromResources("CodeTaskFactory.InvalidElementLocation", node.NodeType, document.Root.Name.LocalName); return(false); } } if (codeElement == null) { // <Code /> element is required so if we didn't find it then we need to error log.LogErrorWithCodeFromResources("CodeTaskFactory.CodeElementIsMissing", taskName); return(false); } // Copies the source code from the inner text of the <Code /> element. This might be override later if the user specified // a file instead. taskInfo.SourceCode = codeElement.Value; // Parse the attributes of the <Code /> element XAttribute languageAttribute = null; XAttribute sourceAttribute = null; XAttribute typeAttribute = null; // TODO: Unit test for this logic and the error message foreach (XAttribute attribute in codeElement.Attributes().Where(i => !i.IsNamespaceDeclaration)) { switch (attribute.Name.LocalName) { case "Language": languageAttribute = attribute; break; case "Source": sourceAttribute = attribute; break; case "Type": typeAttribute = attribute; break; default: log.LogErrorWithCodeFromResources("CodeTaskFactory.InvalidCodeElementAttribute", attribute.Name.LocalName); return(false); } } if (sourceAttribute != null) { if (String.IsNullOrWhiteSpace(sourceAttribute.Value)) { // A <Code Source="" /> is not allowed log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmptyWithElement", "Source", "Code"); return(false); } // Instead of using the inner text of the <Code /> element, read the specified file as source code taskInfo.CodeType = RoslynCodeTaskFactoryCodeType.Class; taskInfo.SourceCode = File.ReadAllText(sourceAttribute.Value.Trim()); } else if (typeAttribute != null) { if (String.IsNullOrWhiteSpace(typeAttribute.Value)) { // A <Code Type="" /> is not allowed log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmptyWithElement", "Type", "Code"); return(false); } // Attempt to parse the code type as a CodeTaskFactoryCodeType if (!Enum.TryParse(typeAttribute.Value.Trim(), ignoreCase: true, result: out RoslynCodeTaskFactoryCodeType codeType)) { log.LogErrorWithCodeFromResources("CodeTaskFactory.InvalidCodeType", typeAttribute.Value, String.Join(", ", Enum.GetNames(typeof(RoslynCodeTaskFactoryCodeType)))); return(false); } taskInfo.CodeType = codeType; } if (languageAttribute != null) { if (String.IsNullOrWhiteSpace(languageAttribute.Value)) { // A <Code Language="" /> is not allowed log.LogErrorWithCodeFromResources("CodeTaskFactory.AttributeEmptyWithElement", "Language", "Code"); return(false); } if (ValidCodeLanguages.ContainsKey(languageAttribute.Value)) { // The user specified one of the primary code languages using our vernacular taskInfo.CodeLanguage = languageAttribute.Value.ToUpperInvariant(); } else { bool foundValidCodeLanguage = false; // Attempt to map the user specified value as an alias to our vernacular for code languages foreach (KeyValuePair <string, ISet <string> > validLanguage in ValidCodeLanguages) { if (validLanguage.Value.Contains(languageAttribute.Value)) { taskInfo.CodeLanguage = validLanguage.Key; foundValidCodeLanguage = true; break; } } if (!foundValidCodeLanguage) { // The user specified a code language we don't support log.LogErrorWithCodeFromResources("CodeTaskFactory.InvalidCodeLanguage", languageAttribute.Value, String.Join(", ", ValidCodeLanguages.Keys)); return(false); } } } if (String.IsNullOrWhiteSpace(taskInfo.SourceCode)) { // The user did not specify a path to source code or source code within the <Code /> element. log.LogErrorWithCodeFromResources("CodeTaskFactory.NoSourceCode"); return(false); } taskInfo.SourceCode = GetSourceCode(taskInfo, parameters); return(true); }
protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) { if (ProvideCommandLineArgs) { CommandLineArgs = GetArguments(commandLineCommands, responseFileCommands) .Select(arg => new TaskItem(arg)).ToArray(); } if (SkipCompilerExecution) { return(0); } try { string workingDir = CurrentDirectoryToUse(); string?tempDir = BuildServerConnection.GetTempPath(workingDir); if (!UseSharedCompilation || HasToolBeenOverridden || !BuildServerConnection.IsCompilerServerSupported) { return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands)); } using var logger = new CompilerServerLogger(); using (_sharedCompileCts = new CancellationTokenSource()) { logger.Log($"CommandLine = '{commandLineCommands}'"); logger.Log($"BuildResponseFile = '{responseFileCommands}'"); var clientDir = Path.GetDirectoryName(PathToManagedTool); if (clientDir is null || tempDir is null) { return(base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands)); } // Note: we can't change the "tool path" printed to the console when we run // the Csc/Vbc task since MSBuild logs it for us before we get here. Instead, // we'll just print our own message that contains the real client location Log.LogMessage(ErrorString.UsingSharedCompilation, clientDir); var buildPaths = new BuildPathsAlt( clientDir: clientDir, workingDir: workingDir, // MSBuild doesn't need the .NET SDK directory sdkDir: null, tempDir: tempDir); // Note: using ToolArguments here (the property) since // commandLineCommands (the parameter) may have been mucked with // (to support using the dotnet cli) var responseTask = BuildServerConnection.RunServerCompilationAsync( Language, RoslynString.IsNullOrEmpty(SharedCompilationId) ? null : SharedCompilationId, GetArguments(ToolArguments, responseFileCommands).ToList(), buildPaths, keepAlive: null, libEnvVariable: LibDirectoryToUse(), logger: logger, cancellationToken: _sharedCompileCts.Token); responseTask.Wait(_sharedCompileCts.Token); var response = responseTask.Result; if (response != null) { ExitCode = HandleResponse(response, pathToTool, responseFileCommands, commandLineCommands, logger); } else { logger.LogError($"Server compilation failed, falling back to {pathToTool}"); Log.LogMessage(ErrorString.SharedCompilationFallback, pathToTool); ExitCode = base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands); } } } catch (OperationCanceledException) { ExitCode = 0; } catch (Exception e) { var util = new TaskLoggingHelper(this); util.LogErrorWithCodeFromResources("Compiler_UnexpectedException"); util.LogErrorFromException(e, showStackTrace: true, showDetail: true, file: null); ExitCode = -1; } return(ExitCode); }