// computes all the inputs and outputs that would be used in the compilation of a project // ensures that all paths are files // ensures no missing inputs public static CompilerIO GetCompileIO(ProjectContext project, string config, string outputPath, string intermediaryOutputPath, ProjectDependenciesFacade dependencies) { var compilerIO = new CompilerIO(new List <string>(), new List <string>()); // input: project.json compilerIO.Inputs.Add(project.ProjectFile.ProjectFilePath); // input: source files compilerIO.Inputs.AddRange(CompilerUtil.GetCompilationSources(project)); // todo: Factor out dependency resolution between Build and Compile. Ideally Build injects the dependencies into Compile // todo: use lock file insteaf of dependencies. One file vs many // input: dependencies AddDependencies(dependencies, compilerIO); // input: key file AddKeyFile(project, config, compilerIO); // output: compiler output compilerIO.Outputs.Add(CompilerUtil.GetCompilationOutput(project.ProjectFile, project.TargetFramework, config, outputPath)); // input / output: resources without culture AddCultureResources(project, intermediaryOutputPath, compilerIO); // input / output: resources with culture AddNonCultureResources(project, outputPath, compilerIO); return(compilerIO); }
private IncrementalResult InputItemsChanged(ProjectGraphNode graphNode, CompilerIO compilerIO) { // check empty inputs / outputs if (!compilerIO.Inputs.Any()) { return(new IncrementalResult("the project has no inputs")); } if (!compilerIO.Outputs.Any()) { return(new IncrementalResult("the project has no outputs")); } // check non existent items var result = CheckMissingIO(compilerIO.Inputs, "inputs"); if (result.NeedsRebuilding) { return(result); } result = CheckMissingIO(compilerIO.Outputs, "outputs"); if (result.NeedsRebuilding) { return(result); } return(CheckInputGlobChanges(graphNode, compilerIO)); }
private IncrementalResult CheckInputGlobChanges(ProjectGraphNode graphNode, CompilerIO compilerIO) { // check cache against input glob pattern changes var incrementalCacheFile = graphNode.ProjectContext.IncrementalCacheFile(_configuration, _buildBasePath, _outputPath); if (!File.Exists(incrementalCacheFile)) { // cache is not present (first compilation); can't determine if globs changed; cache will be generated after build processes project return(IncrementalResult.DoesNotNeedRebuild); } var incrementalCache = IncrementalCache.ReadFromFile(incrementalCacheFile); var diffResult = compilerIO.DiffInputs(incrementalCache.CompilerIO); if (diffResult.Deletions.Any()) { return(new IncrementalResult("Input items removed from last build", diffResult.Deletions)); } if (diffResult.Additions.Any()) { return(new IncrementalResult("Input items added from last build", diffResult.Additions)); } return(IncrementalResult.DoesNotNeedRebuild); }
private static void AddDependencies(ProjectDependenciesFacade dependencies, CompilerIO compilerIO) { // add dependency sources that need compilation compilerIO.Inputs.AddRange(dependencies.ProjectDependenciesWithSources.Values.SelectMany(p => p.Project.Files.SourceFiles)); // non project dependencies get captured by changes in the lock file }
// computes all the inputs and outputs that would be used in the compilation of a project // ensures that all paths are files // ensures no missing inputs public static CompilerIO GetCompileIO(ProjectContext project, string config, string outputPath, string intermediaryOutputPath, ProjectDependenciesFacade dependencies) { var compilerIO = new CompilerIO(new List<string>(), new List<string>()); var compilationOutput = CompilerUtil.GetCompilationOutput(project.ProjectFile, project.TargetFramework, config, outputPath); // input: project.json compilerIO.Inputs.Add(project.ProjectFile.ProjectFilePath); // input: lock file; find when dependencies change AddLockFile(project, compilerIO); // input: source files compilerIO.Inputs.AddRange(CompilerUtil.GetCompilationSources(project)); // todo: Factor out dependency resolution between Build and Compile. Ideally Build injects the dependencies into Compile // input: dependencies AddDependencies(dependencies, compilerIO); // output: compiler output compilerIO.Outputs.Add(compilationOutput); // input / output: compilation options files AddFilesFromCompilationOptions(project, config, compilationOutput, compilerIO); // input / output: resources without culture AddCultureResources(project, intermediaryOutputPath, compilerIO); // input / output: resources with culture AddNonCultureResources(project, outputPath, compilerIO); return compilerIO; }
private static void AddDependencies(ProjectDependenciesFacade dependencies, CompilerIO compilerIO) { // add dependency sources that need compilation compilerIO.Inputs.AddRange(dependencies.ProjectDependenciesWithSources.Values.SelectMany(p => p.Project.Files.SourceFiles)); // add compilation binaries compilerIO.Inputs.AddRange(dependencies.Dependencies.SelectMany(d => d.CompilationAssemblies.Select(ca => ca.ResolvedPath))); }
private static void AddKeyFile(ProjectContext project, string config, CompilerIO compilerIO) { var keyFile = CompilerUtil.ResolveCompilationOptions(project, config).KeyFile; if (keyFile != null) { compilerIO.Inputs.Add(keyFile); } }
public DiffResult DiffInputs(CompilerIO other) { var myInputSet = new HashSet<string>(Inputs); var otherInputSet = new HashSet<string>(other.Inputs); var additions = myInputSet.Except(otherInputSet); var deletions = otherInputSet.Except(myInputSet); return new DiffResult(additions, deletions); }
public DiffResult DiffInputs(CompilerIO other) { var myInputSet = new HashSet <string>(Inputs); var otherInputSet = new HashSet <string>(other.Inputs); var additions = myInputSet.Except(otherInputSet); var deletions = otherInputSet.Except(myInputSet); return(new DiffResult(additions, deletions)); }
private static void AddLockFile(ProjectContext project, CompilerIO compilerIO) { if (project.LockFile == null) { var errorMessage = $"Project {project.ProjectName()} does not have a lock file."; Reporter.Error.WriteLine(errorMessage); throw new InvalidOperationException(errorMessage); } compilerIO.Inputs.Add(project.LockFile.LockFilePath); }
// computes all the inputs and outputs that would be used in the compilation of a project // ensures that all paths are files // ensures no missing inputs public CompilerIO GetCompileIO(ProjectContext project, ProjectDependenciesFacade dependencies) { var buildConfiguration = _args.ConfigValue; var buildBasePath = _args.BuildBasePathValue; var outputPath = _args.OutputValue; var isRootProject = project == _rootProject; var compilerIO = new CompilerIO(new List <string>(), new List <string>()); var calculator = project.GetOutputPaths(buildConfiguration, buildBasePath, outputPath); var binariesOutputPath = calculator.CompilationOutputPath; // input: project.json compilerIO.Inputs.Add(project.ProjectFile.ProjectFilePath); // input: lock file; find when dependencies change AddLockFile(project, compilerIO); // input: source files compilerIO.Inputs.AddRange(CompilerUtil.GetCompilationSources(project)); // todo: Factor out dependency resolution between Build and Compile. Ideally Build injects the dependencies into Compile // input: dependencies AddDependencies(dependencies, compilerIO); var allOutputPath = new HashSet <string>(calculator.CompilationFiles.All()); if (isRootProject && project.ProjectFile.HasRuntimeOutput(buildConfiguration)) { var runtimeContext = project.CreateRuntimeContext(_args.GetRuntimes()); foreach (var path in runtimeContext.GetOutputPaths(buildConfiguration, buildBasePath, outputPath).RuntimeFiles.All()) { allOutputPath.Add(path); } } // output: compiler outputs foreach (var path in allOutputPath) { compilerIO.Outputs.Add(path); } // input compilation options files AddCompilationOptions(project, buildConfiguration, compilerIO); // input / output: resources with culture AddNonCultureResources(project, calculator.IntermediateOutputDirectoryPath, compilerIO); // input / output: resources without culture AddCultureResources(project, binariesOutputPath, compilerIO); return(compilerIO); }
// computes all the inputs and outputs that would be used in the compilation of a project // ensures that all paths are files // ensures no missing inputs public CompilerIO GetCompileIO(ProjectContext project, ProjectDependenciesFacade dependencies) { var buildConfiguration = _args.ConfigValue; var buildBasePath = _args.BuildBasePathValue; var outputPath = _args.OutputValue; var isRootProject = project == _rootProject; var compilerIO = new CompilerIO(new List<string>(), new List<string>()); var calculator = project.GetOutputPaths(buildConfiguration, buildBasePath, outputPath); var binariesOutputPath = calculator.CompilationOutputPath; // input: project.json compilerIO.Inputs.Add(project.ProjectFile.ProjectFilePath); // input: lock file; find when dependencies change AddLockFile(project, compilerIO); // input: source files compilerIO.Inputs.AddRange(CompilerUtil.GetCompilationSources(project)); // todo: Factor out dependency resolution between Build and Compile. Ideally Build injects the dependencies into Compile // input: dependencies AddDependencies(dependencies, compilerIO); var allOutputPath = new List<string>(calculator.CompilationFiles.All()); if (isRootProject && project.ProjectFile.HasRuntimeOutput(buildConfiguration)) { var runtimeContext = project.CreateRuntimeContext(_args.GetRuntimes()); allOutputPath.AddRange(runtimeContext.GetOutputPaths(buildConfiguration, buildBasePath, outputPath).RuntimeFiles.All()); } // output: compiler outputs foreach (var path in allOutputPath) { compilerIO.Outputs.Add(path); } // input compilation options files AddCompilationOptions(project, buildConfiguration, compilerIO); // input / output: resources with culture AddNonCultureResources(project, calculator.IntermediateOutputDirectoryPath, compilerIO); // input / output: resources without culture AddCultureResources(project, binariesOutputPath, compilerIO); return compilerIO; }
// computes all the inputs and outputs that would be used in the compilation of a project // ensures that all paths are files // ensures no missing inputs public static CompilerIO GetCompileIO( ProjectContext project, string buildConfiguration, string outputPath, string intermediaryOutputPath, ProjectDependenciesFacade dependencies) { var compilerIO = new CompilerIO(new List <string>(), new List <string>()); var calculator = project.GetOutputPathCalculator(outputPath); var binariesOutputPath = calculator.GetOutputDirectoryPath(buildConfiguration); intermediaryOutputPath = calculator.GetIntermediateOutputDirectoryPath(buildConfiguration, intermediaryOutputPath); // input: project.json compilerIO.Inputs.Add(project.ProjectFile.ProjectFilePath); // input: lock file; find when dependencies change AddLockFile(project, compilerIO); // input: source files compilerIO.Inputs.AddRange(CompilerUtil.GetCompilationSources(project)); // todo: Factor out dependency resolution between Build and Compile. Ideally Build injects the dependencies into Compile // input: dependencies AddDependencies(dependencies, compilerIO); // output: compiler outputs foreach (var path in calculator.GetBuildOutputs(buildConfiguration)) { compilerIO.Outputs.Add(path); } // input compilation options files AddCompilationOptions(project, buildConfiguration, compilerIO); // input / output: resources without culture AddCultureResources(project, intermediaryOutputPath, compilerIO); // input / output: resources with culture AddNonCultureResources(project, binariesOutputPath, compilerIO); return(compilerIO); }
private IncrementalResult CheckInputGlobChanges(ProjectGraphNode graphNode, CompilerIO compilerIO) { // check cache against input glob pattern changes var incrementalCacheFile = graphNode.ProjectContext.IncrementalCacheFile(_configuration, _buildBasePath, _outputPath); if (!File.Exists(incrementalCacheFile)) { // cache is not present (first compilation); can't determine if globs changed; cache will be generated after build processes project return(IncrementalResult.DoesNotNeedRebuild); } var incrementalCache = IncrementalCache.ReadFromFile(incrementalCacheFile); var diffResult = compilerIO.DiffInputs(incrementalCache.CompilerIO); if (diffResult.Deletions.Any()) { return(new IncrementalResult("Input items removed from last build", diffResult.Deletions)); } if (diffResult.Additions.Any()) { return(new IncrementalResult("Input items added from last build", diffResult.Additions)); } var keys = incrementalCache.BuildArguments.Keys.Union(_incrementalAffectingArguments.Keys); var mismatchedKeys = keys.Where(k => { string cachedVal; string currentVal; return(!incrementalCache.BuildArguments.TryGetValue(k, out cachedVal) || !_incrementalAffectingArguments.TryGetValue(k, out currentVal) || !string.Equals(cachedVal ?? string.Empty, currentVal ?? string.Empty, StringComparison.Ordinal)); }); if (mismatchedKeys.Any()) { return(new IncrementalResult("Build arguments changed since last build", mismatchedKeys)); } return(IncrementalResult.DoesNotNeedRebuild); }
// computes all the inputs and outputs that would be used in the compilation of a project // ensures that all paths are files // ensures no missing inputs public static CompilerIO GetCompileIO( ProjectContext project, string buildConfiguration, string outputPath, string intermediaryOutputPath, ProjectDependenciesFacade dependencies) { var compilerIO = new CompilerIO(new List<string>(), new List<string>()); var calculator = project.GetOutputPathCalculator(outputPath); var binariesOutputPath = calculator.GetOutputDirectoryPath(buildConfiguration); // input: project.json compilerIO.Inputs.Add(project.ProjectFile.ProjectFilePath); // input: lock file; find when dependencies change AddLockFile(project, compilerIO); // input: source files compilerIO.Inputs.AddRange(CompilerUtil.GetCompilationSources(project)); // todo: Factor out dependency resolution between Build and Compile. Ideally Build injects the dependencies into Compile // input: dependencies AddDependencies(dependencies, compilerIO); // output: compiler outputs foreach (var path in calculator.GetBuildOutputs(buildConfiguration)) { compilerIO.Outputs.Add(path); } // input compilation options files AddCompilationOptions(project, buildConfiguration, compilerIO); // input / output: resources without culture AddCultureResources(project, intermediaryOutputPath, compilerIO); // input / output: resources with culture AddNonCultureResources(project, binariesOutputPath, compilerIO); return compilerIO; }
private IncrementalResult TimestampsChanged(CompilerIO compilerIO) { // find the output with the earliest write time var minDateUtc = DateTime.MaxValue; foreach (var outputPath in compilerIO.Outputs) { var lastWriteTimeUtc = File.GetLastWriteTimeUtc(outputPath); if (lastWriteTimeUtc < minDateUtc) { minDateUtc = lastWriteTimeUtc; } } // find inputs that are older than the earliest output var newInputs = compilerIO.Inputs.Where(p => File.GetLastWriteTimeUtc(p) >= minDateUtc); return(newInputs.Any() ? new IncrementalResult("inputs were modified", newInputs) : IncrementalResult.DoesNotNeedRebuild); }
private static void AddCultureResources(ProjectContext project, string outputPath, CompilerIO compilerIO) { foreach (var cultureResourceIO in CompilerUtil.GetCultureResources(project.ProjectFile, outputPath)) { compilerIO.Inputs.AddRange(cultureResourceIO.InputFileToMetadata.Keys); if (cultureResourceIO.OutputFile != null) { compilerIO.Outputs.Add(cultureResourceIO.OutputFile); } } }
private static void AddCompilationOptions(ProjectContext project, string config, CompilerIO compilerIO) { var compilerOptions = CompilerUtil.ResolveCompilationOptions(project, config); // input: key file if (compilerOptions.KeyFile != null) { compilerIO.Inputs.Add(compilerOptions.KeyFile); } }
private IncrementalResult TimestampsChanged(CompilerIO compilerIO) { // find the output with the earliest write time var minDateUtc = DateTime.MaxValue; foreach (var outputPath in compilerIO.Outputs) { var lastWriteTimeUtc = File.GetLastWriteTimeUtc(outputPath); if (lastWriteTimeUtc < minDateUtc) { minDateUtc = lastWriteTimeUtc; } } // find inputs that are newer than the earliest output var newInputs = compilerIO.Inputs.Where(p => File.GetLastWriteTimeUtc(p) >= minDateUtc); return newInputs.Any() ? new IncrementalResult("inputs were modified", newInputs) : IncrementalResult.DoesNotNeedRebuild; }
private IncrementalResult CheckInputGlobChanges(ProjectGraphNode graphNode, CompilerIO compilerIO) { // check cache against input glob pattern changes var incrementalCacheFile = graphNode.ProjectContext.IncrementalCacheFile(_configuration, _buildBasePath, _outputPath); if (!File.Exists(incrementalCacheFile)) { // cache is not present (first compilation); can't determine if globs changed; cache will be generated after build processes project return IncrementalResult.DoesNotNeedRebuild; } var incrementalCache = IncrementalCache.ReadFromFile(incrementalCacheFile); var diffResult = compilerIO.DiffInputs(incrementalCache.CompilerIO); if (diffResult.Deletions.Any()) { return new IncrementalResult("Input items removed from last build", diffResult.Deletions); } if (diffResult.Additions.Any()) { return new IncrementalResult("Input items added from last build", diffResult.Additions); } var keys = incrementalCache.BuildArguments.Keys.Union(_incrementalAffectingArguments.Keys); var mismatchedKeys = keys.Where(k => { string cachedVal; string currentVal; return !incrementalCache.BuildArguments.TryGetValue(k, out cachedVal) || !_incrementalAffectingArguments.TryGetValue(k, out currentVal) || !string.Equals(cachedVal ?? string.Empty, currentVal ?? string.Empty, StringComparison.Ordinal); }); if (mismatchedKeys.Any()) { return new IncrementalResult("Build arguments changed since last build", mismatchedKeys); } return IncrementalResult.DoesNotNeedRebuild; }
public IncrementalCache(CompilerIO compilerIO, IEnumerable <KeyValuePair <string, string> > parameters) { CompilerIO = compilerIO; BuildArguments = parameters.ToDictionary(p => p.Key, p => p.Value); }
private static void AddCompilationOptions(ProjectContext project, string config, CompilerIO compilerIO) { var compilerOptions = project.ResolveCompilationOptions(config); // input: key file if (compilerOptions.KeyFile != null) { compilerIO.Inputs.Add(compilerOptions.KeyFile); } }
private static void AddFilesFromCompilationOptions(ProjectContext project, string config, string compilationOutput, CompilerIO compilerIO) { var compilerOptions = CompilerUtil.ResolveCompilationOptions(project, config); // output: pdb file. They are always emitted (see compiler.csc) compilerIO.Outputs.Add(Path.ChangeExtension(compilationOutput, "pdb")); // output: documentation file if (compilerOptions.GenerateXmlDocumentation == true) { compilerIO.Outputs.Add(Path.ChangeExtension(compilationOutput, "xml")); } // input: key file if (compilerOptions.KeyFile != null) { compilerIO.Inputs.Add(compilerOptions.KeyFile); } }
private static void AddNonCultureResources(ProjectContext project, string intermediaryOutputPath, CompilerIO compilerIO) { foreach (var resourceIO in CompilerUtil.GetNonCultureResources(project.ProjectFile, intermediaryOutputPath)) { compilerIO.Inputs.Add(resourceIO.InputFile); if (resourceIO.OutputFile != null) { compilerIO.Outputs.Add(resourceIO.OutputFile); } } }
private IncrementalResult InputItemsChanged(ProjectGraphNode graphNode, CompilerIO compilerIO) { // check empty inputs / outputs if (!compilerIO.Inputs.Any()) { return new IncrementalResult("the project has no inputs"); } if (!compilerIO.Outputs.Any()) { return new IncrementalResult("the project has no outputs"); } // check non existent items var result = CheckMissingIO(compilerIO.Inputs, "inputs"); if (result.NeedsRebuilding) { return result; } result = CheckMissingIO(compilerIO.Outputs, "outputs"); if (result.NeedsRebuilding) { return result; } return CheckInputGlobChanges(graphNode, compilerIO); }
public IncrementalCache(CompilerIO compilerIO) { CompilerIO = compilerIO; }