private ToolChainSelection SelectToolChain() { // See if user requested a specific toolchain via -toolchain= or output file extension ToolChainSelection requestedToolChainType = new ToolChainSelection(); if (_requestedToolChain != null) { if (!ToolChain.TryGetToolchain(_requestedToolChain, out Type requestedType)) { throw Diagnostics.ThrowError( $"{_requestedToolChain} is not a supported toolchain", writer => { writer.WriteLine("Accepted toolchains:"); ToolChain.WriteOutToolChains(writer); }); } requestedToolChainType = new ToolChainSelection(_requestedToolChain, requestedType); } // When an explicit toolchain is not selected, build a closed set of incompatible toolchains HashSet <string> closedToolChains = new HashSet <string>(ToolChain.ToolChains.Length); void CheckFileToolChainCompatibilty(FileInfo fileInfo) { if (requestedToolChainType.Value != null) { // Ensure file is compatible with user-requested toolchain if (!fileInfo.IsCompatibleWithToolchainType(requestedToolChainType.Value)) { Diagnostics.Report(Diagnostics.Severity.Error, $"{fileInfo.OriginalPath} is not compatible with {requestedToolChainType.Key}"); } } else { // Add incompatible toolchains to closed set foreach (var pair in ToolChain.ToolChains) { if (!fileInfo.IsCompatibleWithToolchainType(pair.Value)) { closedToolChains.Add(pair.Key); } } } } // If specified, eliminate incompatible toolchains based on output file extension if (_outputFile != null && _outputFile.Kind != FileType.Kind.UNCLASSIFIED) { CheckFileToolChainCompatibilty(_outputFile); } // Ensure each input file requires a consistent toolchain foreach (FileInfo inputFile in _inputFiles) { CheckFileToolChainCompatibilty(inputFile); } Diagnostics.ThrowIfErrors(); // Toolchain set as user request if (requestedToolChainType.Value != null) { return(requestedToolChainType); } // Otherwise take first non-closed toolchain foreach (var pair in ToolChain.ToolChains) { if (closedToolChains.Contains(pair.Key)) { continue; } return(pair); } throw Diagnostics.ThrowError("unable to select a mutually compatible toolchain with provided files"); }
public Job(CommandLine commandLine) { commandLine.ApplyTo(this); // Build input file list _inputFiles = new List <FileInfo>(commandLine.PositionalArguments.Count + (_interactive ? 1 : 0)); foreach (string filePath in commandLine.PositionalArguments) { FileInfo tmpFile = new FileInfo(filePath); if (!tmpFile.Exists) { Diagnostics.Report(Diagnostics.Severity.Error, $"{filePath} does not exist"); } else if (tmpFile.Kind == FileType.Kind.UNCLASSIFIED) { Diagnostics.Report(Diagnostics.Severity.Error, $"{filePath} is of unknown type"); } else if (tmpFile.Kind == FileType.Kind.TARGET_ASSEMBLY || tmpFile.Kind == FileType.Kind.TARGET_OBJECT) { Diagnostics.Report(Diagnostics.Severity.Error, "MonC cannot load target assembly or object files. External tools must be used for these."); } else { _inputFiles.Add(tmpFile); } } // Additional interactive input file if requested if (_interactive) { _inputFiles.Add(FileInfo.Interactive); } Diagnostics.ThrowIfErrors(); // Skip run is implied when output path is provided if (_outputPath != null) { _skipRun = true; } // VM is implied for debugger if (_debugger) { _skipRun = false; } if (_asm) { // Relocatable processing is implied for assembly output _reloc = true; } // Relocatable output always skips VM and may only accept one input file if (_reloc) { _skipRun = true; if (_inputFiles.Count > 1) { throw Diagnostics.ThrowError("-c or -S flag may only be set for one input file"); } } // Set output file if provided and not using VM _outputFile = _outputPath != null ? new FileInfo(_outputPath) : null; // Assembly output defaults to stdout if not specified if (_asm && _outputFile == null) { _outputFile = FileInfo.StdIo; } // Select toolchain by user request or input files / output file _toolChainSelection = SelectToolChain(); // Construct toolchain _toolChain = (ToolChain)Activator.CreateInstance(_toolChainSelection.Value); commandLine.ApplyTo(_toolChain); // Determine target phase based on output file (if provided) and command line flags _targetPhase = SelectTargetPhase(); if (_reloc && _inputFiles.Count > 1) { throw Diagnostics.ThrowError("relocatable output only works with one input file"); } // Initialize module phase open set with pre-link phases up to target PhaseSet modulePhaseOpenSet = _toolChain.FilterPhases( PhaseSet.AllPhasesTo((Phase)Math.Min((int)_targetPhase, (int)Phase.Link - 1))); // Visit JobActions for each file; building a chain of tools _moduleFileTools = new List <IModuleTool>(_inputFiles.Count); foreach (FileInfo inputFile in _inputFiles) { if (inputFile.IsLinkerInput) { _moduleFileTools.Add(_toolChain.BuildLinkerInputFileTool(this, inputFile)); continue; } ITool tool = null; foreach (Phase phase in inputFile.PossiblePhases& modulePhaseOpenSet) { tool = IJobAction.FromPhase(phase) .Accept(_toolChain, this, tool != null ? (IInput)tool : inputFile); commandLine.ApplyTo(tool); } if (tool is IModuleTool moduleTool) { _moduleFileTools.Add(moduleTool); } else { throw new InvalidOperationException("toolchain did not produce a module input file tool"); } } // Set up link and subsequent tools if (_targetPhase >= Phase.Link) { PhaseSet linkPhaseOpenSet = _toolChain.FilterPhases(PhaseSet.AllPhasesTo(_targetPhase) & PhaseSet.AllPhasesFrom(Phase.Link)); ITool tool = null; foreach (Phase phase in linkPhaseOpenSet) { tool = IJobAction.FromPhase(phase) .Accept(_toolChain, this, tool != null ? (IInput)tool : this); commandLine.ApplyTo(tool); } if (tool is IExecutableTool executableTool) { _executableTool = executableTool; } else { throw new InvalidOperationException("toolchain did not produce an executable tool"); } } }