| //===--- FrontendInputsAndOutputs.cpp -------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/Frontend/FrontendInputsAndOutputs.h" |
| |
| #include "swift/AST/DiagnosticsFrontend.h" |
| #include "swift/Basic/FileTypes.h" |
| #include "swift/Basic/PrimarySpecificPaths.h" |
| #include "swift/Basic/Range.h" |
| #include "swift/Frontend/FrontendOptions.h" |
| #include "swift/Option/Options.h" |
| #include "swift/Parse/Lexer.h" |
| #include "swift/Strings.h" |
| #include "llvm/Option/Arg.h" |
| #include "llvm/Option/ArgList.h" |
| #include "llvm/Option/Option.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/LineIterator.h" |
| #include "llvm/Support/Path.h" |
| |
| using namespace swift; |
| using namespace llvm::opt; |
| |
| // Constructors |
| |
| FrontendInputsAndOutputs::FrontendInputsAndOutputs( |
| const FrontendInputsAndOutputs &other) { |
| for (InputFile input : other.AllInputs) |
| addInput(input); |
| IsSingleThreadedWMO = other.IsSingleThreadedWMO; |
| ShouldRecoverMissingInputs = other.ShouldRecoverMissingInputs; |
| } |
| |
| FrontendInputsAndOutputs &FrontendInputsAndOutputs:: |
| operator=(const FrontendInputsAndOutputs &other) { |
| clearInputs(); |
| for (InputFile input : other.AllInputs) |
| addInput(input); |
| IsSingleThreadedWMO = other.IsSingleThreadedWMO; |
| ShouldRecoverMissingInputs = other.ShouldRecoverMissingInputs; |
| return *this; |
| } |
| |
| // All inputs: |
| |
| std::vector<std::string> FrontendInputsAndOutputs::getInputFilenames() const { |
| std::vector<std::string> filenames; |
| for (auto &input : AllInputs) { |
| filenames.push_back(input.getFileName()); |
| } |
| return filenames; |
| } |
| |
| bool FrontendInputsAndOutputs::isReadingFromStdin() const { |
| return hasSingleInput() && getFilenameOfFirstInput() == "-"; |
| } |
| |
| const std::string &FrontendInputsAndOutputs::getFilenameOfFirstInput() const { |
| assert(hasInputs()); |
| const InputFile &inp = AllInputs[0]; |
| const std::string &f = inp.getFileName(); |
| assert(!f.empty()); |
| return f; |
| } |
| |
| bool FrontendInputsAndOutputs::forEachInput( |
| llvm::function_ref<bool(const InputFile &)> fn) const { |
| for (const InputFile &input : AllInputs) |
| if (fn(input)) |
| return true; |
| return false; |
| } |
| |
| // Primaries: |
| |
| const InputFile &FrontendInputsAndOutputs::firstPrimaryInput() const { |
| assert(!PrimaryInputsInOrder.empty()); |
| return AllInputs[PrimaryInputsInOrder.front()]; |
| } |
| |
| const InputFile &FrontendInputsAndOutputs::lastPrimaryInput() const { |
| assert(!PrimaryInputsInOrder.empty()); |
| return AllInputs[PrimaryInputsInOrder.back()]; |
| } |
| |
| bool FrontendInputsAndOutputs::forEachPrimaryInput( |
| llvm::function_ref<bool(const InputFile &)> fn) const { |
| for (unsigned i : PrimaryInputsInOrder) |
| if (fn(AllInputs[i])) |
| return true; |
| return false; |
| } |
| |
| bool FrontendInputsAndOutputs::forEachPrimaryInputWithIndex( |
| llvm::function_ref<bool(const InputFile &, unsigned index)> fn) const { |
| for (unsigned i : PrimaryInputsInOrder) |
| if (fn(AllInputs[i], i)) |
| return true; |
| return false; |
| } |
| |
| bool FrontendInputsAndOutputs::forEachNonPrimaryInput( |
| llvm::function_ref<bool(const InputFile &)> fn) const { |
| return forEachInput([&](const InputFile &f) -> bool { |
| return f.isPrimary() ? false : fn(f); |
| }); |
| } |
| |
| void FrontendInputsAndOutputs::assertMustNotBeMoreThanOnePrimaryInput() const { |
| assert(!hasMultiplePrimaryInputs() && |
| "have not implemented >1 primary input yet"); |
| } |
| |
| void FrontendInputsAndOutputs:: |
| assertMustNotBeMoreThanOnePrimaryInputUnlessBatchModeChecksHaveBeenBypassed() |
| const { |
| if (!areBatchModeChecksBypassed()) |
| assertMustNotBeMoreThanOnePrimaryInput(); |
| } |
| |
| const InputFile *FrontendInputsAndOutputs::getUniquePrimaryInput() const { |
| assertMustNotBeMoreThanOnePrimaryInput(); |
| return PrimaryInputsInOrder.empty() |
| ? nullptr |
| : &AllInputs[PrimaryInputsInOrder.front()]; |
| } |
| |
| const InputFile & |
| FrontendInputsAndOutputs::getRequiredUniquePrimaryInput() const { |
| if (const auto *input = getUniquePrimaryInput()) |
| return *input; |
| llvm_unreachable("No primary when one is required"); |
| } |
| |
| std::string FrontendInputsAndOutputs::getStatsFileMangledInputName() const { |
| // Use the first primary, even if there are multiple primaries. |
| // That's enough to keep the file names unique. |
| return isWholeModule() ? "all" : firstPrimaryInput().getFileName(); |
| } |
| |
| bool FrontendInputsAndOutputs::isInputPrimary(StringRef file) const { |
| return primaryInputNamed(file) != nullptr; |
| } |
| |
| unsigned FrontendInputsAndOutputs::numberOfPrimaryInputsEndingWith( |
| StringRef extension) const { |
| unsigned n = 0; |
| (void)forEachPrimaryInput([&](const InputFile &input) -> bool { |
| if (llvm::sys::path::extension(input.getFileName()).endswith(extension)) |
| ++n; |
| return false; |
| }); |
| return n; |
| } |
| |
| // Input queries |
| |
| bool FrontendInputsAndOutputs::shouldTreatAsLLVM() const { |
| if (hasSingleInput()) { |
| StringRef InputExt = llvm::sys::path::extension(getFilenameOfFirstInput()); |
| switch (file_types::lookupTypeForExtension(InputExt)) { |
| case file_types::TY_LLVM_BC: |
| case file_types::TY_LLVM_IR: |
| return true; |
| default: |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| bool FrontendInputsAndOutputs::shouldTreatAsModuleInterface() const { |
| if (!hasSingleInput()) |
| return false; |
| |
| StringRef InputExt = llvm::sys::path::extension(getFilenameOfFirstInput()); |
| file_types::ID InputType = file_types::lookupTypeForExtension(InputExt); |
| return InputType == file_types::TY_SwiftModuleInterfaceFile; |
| } |
| |
| bool FrontendInputsAndOutputs::shouldTreatAsSIL() const { |
| if (hasSingleInput()) { |
| // If we have exactly one input filename, and its extension is "sil", |
| // treat the input as SIL. |
| StringRef extension = llvm::sys::path::extension(getFilenameOfFirstInput()); |
| return file_types::lookupTypeForExtension(extension) == file_types::TY_SIL; |
| } |
| // If we have one primary input and it's a filename with extension "sil", |
| // treat the input as SIL. |
| const unsigned silPrimaryCount = numberOfPrimaryInputsEndingWith( |
| file_types::getExtension(file_types::TY_SIL)); |
| if (silPrimaryCount == 0) |
| return false; |
| if (silPrimaryCount == primaryInputCount()) { |
| // Not clear what to do someday with multiple primaries |
| assertMustNotBeMoreThanOnePrimaryInput(); |
| return true; |
| } |
| llvm_unreachable("Either all primaries or none must end with .sil"); |
| } |
| |
| bool FrontendInputsAndOutputs::shouldTreatAsObjCHeader() const { |
| if (hasSingleInput()) { |
| StringRef InputExt = llvm::sys::path::extension(getFilenameOfFirstInput()); |
| switch (file_types::lookupTypeForExtension(InputExt)) { |
| case file_types::TY_ObjCHeader: |
| return true; |
| default: |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| bool FrontendInputsAndOutputs::areAllNonPrimariesSIB() const { |
| for (const InputFile &input : AllInputs) { |
| if (input.isPrimary()) |
| continue; |
| StringRef extension = llvm::sys::path::extension(input.getFileName()); |
| if (file_types::lookupTypeForExtension(extension) != file_types::TY_SIB) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool FrontendInputsAndOutputs::verifyInputs(DiagnosticEngine &diags, |
| bool treatAsSIL, |
| bool isREPLRequested, |
| bool isNoneRequested) const { |
| if (isREPLRequested) { |
| if (hasInputs()) { |
| diags.diagnose(SourceLoc(), diag::error_repl_requires_no_input_files); |
| return true; |
| } |
| } else if (treatAsSIL) { |
| if (isWholeModule()) { |
| if (inputCount() != 1) { |
| diags.diagnose(SourceLoc(), diag::error_mode_requires_one_input_file); |
| return true; |
| } |
| } else { |
| assertMustNotBeMoreThanOnePrimaryInput(); |
| // If we have the SIL as our primary input, we can waive the one file |
| // requirement as long as all the other inputs are SIBs. |
| if (!areAllNonPrimariesSIB()) { |
| diags.diagnose(SourceLoc(), |
| diag::error_mode_requires_one_sil_multi_sib); |
| return true; |
| } |
| } |
| } else if (!isNoneRequested && !hasInputs()) { |
| diags.diagnose(SourceLoc(), diag::error_mode_requires_an_input_file); |
| return true; |
| } |
| return false; |
| } |
| |
| // Changing inputs |
| |
| void FrontendInputsAndOutputs::clearInputs() { |
| AllInputs.clear(); |
| PrimaryInputsByName.clear(); |
| PrimaryInputsInOrder.clear(); |
| } |
| |
| void FrontendInputsAndOutputs::addInput(const InputFile &input) { |
| const unsigned index = AllInputs.size(); |
| AllInputs.push_back(input); |
| if (input.isPrimary()) { |
| PrimaryInputsInOrder.push_back(index); |
| PrimaryInputsByName.insert({AllInputs.back().getFileName(), index}); |
| } |
| } |
| |
| void FrontendInputsAndOutputs::addInputFile(StringRef file, |
| llvm::MemoryBuffer *buffer) { |
| addInput(InputFile(file, false, buffer)); |
| } |
| |
| void FrontendInputsAndOutputs::addPrimaryInputFile(StringRef file, |
| llvm::MemoryBuffer *buffer) { |
| addInput(InputFile(file, true, buffer)); |
| } |
| |
| // Outputs |
| |
| unsigned FrontendInputsAndOutputs::countOfInputsProducingMainOutputs() const { |
| return isSingleThreadedWMO() |
| ? 1 |
| : hasPrimaryInputs() ? primaryInputCount() : inputCount(); |
| } |
| |
| const InputFile &FrontendInputsAndOutputs::firstInputProducingOutput() const { |
| return isSingleThreadedWMO() |
| ? firstInput() |
| : hasPrimaryInputs() ? firstPrimaryInput() : firstInput(); |
| } |
| |
| const InputFile &FrontendInputsAndOutputs::lastInputProducingOutput() const { |
| return isSingleThreadedWMO() |
| ? firstInput() |
| : hasPrimaryInputs() ? lastPrimaryInput() : lastInput(); |
| } |
| |
| bool FrontendInputsAndOutputs::forEachInputProducingAMainOutputFile( |
| llvm::function_ref<bool(const InputFile &)> fn) const { |
| return isSingleThreadedWMO() |
| ? fn(firstInput()) |
| : hasPrimaryInputs() ? forEachPrimaryInput(fn) : forEachInput(fn); |
| } |
| |
| void FrontendInputsAndOutputs::setMainAndSupplementaryOutputs( |
| ArrayRef<std::string> outputFiles, |
| ArrayRef<SupplementaryOutputPaths> supplementaryOutputs) { |
| if (AllInputs.empty()) { |
| assert(outputFiles.empty() && "Cannot have outputs without inputs"); |
| assert(supplementaryOutputs.empty() && |
| "Cannot have supplementary outputs without inputs"); |
| return; |
| } |
| if (hasPrimaryInputs()) { |
| const auto N = primaryInputCount(); |
| assert(outputFiles.size() == N && "Must have one main output per primary"); |
| assert(supplementaryOutputs.size() == N && |
| "Must have one set of supplementary outputs per primary"); |
| (void)N; |
| |
| unsigned i = 0; |
| for (auto &input : AllInputs) { |
| if (input.isPrimary()) { |
| input.setPrimarySpecificPaths(PrimarySpecificPaths( |
| outputFiles[i], input.getFileName(), supplementaryOutputs[i])); |
| ++i; |
| } |
| } |
| return; |
| } |
| assert(supplementaryOutputs.size() == 1 && |
| "WMO only ever produces one set of supplementary outputs"); |
| if (outputFiles.size() == 1) { |
| AllInputs.front().setPrimarySpecificPaths(PrimarySpecificPaths( |
| outputFiles.front(), firstInputProducingOutput().getFileName(), |
| supplementaryOutputs.front())); |
| return; |
| } |
| assert(outputFiles.size() == AllInputs.size() && |
| "Multi-threaded WMO requires one main output per input"); |
| for (auto i : indices(AllInputs)) |
| AllInputs[i].setPrimarySpecificPaths(PrimarySpecificPaths( |
| outputFiles[i], outputFiles[i], |
| i == 0 ? supplementaryOutputs.front() : SupplementaryOutputPaths())); |
| } |
| |
| std::vector<std::string> FrontendInputsAndOutputs::copyOutputFilenames() const { |
| std::vector<std::string> outputs; |
| (void)forEachInputProducingAMainOutputFile( |
| [&](const InputFile &input) -> bool { |
| outputs.push_back(input.outputFilename()); |
| return false; |
| }); |
| return outputs; |
| } |
| |
| void FrontendInputsAndOutputs::forEachOutputFilename( |
| llvm::function_ref<void(StringRef)> fn) const { |
| (void)forEachInputProducingAMainOutputFile( |
| [&](const InputFile &input) -> bool { |
| fn(input.outputFilename()); |
| return false; |
| }); |
| } |
| |
| std::string FrontendInputsAndOutputs::getSingleOutputFilename() const { |
| assertMustNotBeMoreThanOnePrimaryInputUnlessBatchModeChecksHaveBeenBypassed(); |
| return hasInputs() ? lastInputProducingOutput().outputFilename() |
| : std::string(); |
| } |
| |
| bool FrontendInputsAndOutputs::isOutputFilenameStdout() const { |
| return getSingleOutputFilename() == "-"; |
| } |
| |
| bool FrontendInputsAndOutputs::isOutputFileDirectory() const { |
| return hasNamedOutputFile() && |
| llvm::sys::fs::is_directory(getSingleOutputFilename()); |
| } |
| |
| bool FrontendInputsAndOutputs::hasNamedOutputFile() const { |
| return hasInputs() && !isOutputFilenameStdout(); |
| } |
| |
| // Supplementary outputs |
| |
| unsigned |
| FrontendInputsAndOutputs::countOfFilesProducingSupplementaryOutput() const { |
| return hasPrimaryInputs() ? primaryInputCount() : hasInputs() ? 1 : 0; |
| } |
| |
| bool FrontendInputsAndOutputs::forEachInputProducingSupplementaryOutput( |
| llvm::function_ref<bool(const InputFile &)> fn) const { |
| return hasPrimaryInputs() ? forEachPrimaryInput(fn) |
| : hasInputs() ? fn(firstInput()) : false; |
| } |
| |
| bool FrontendInputsAndOutputs::hasSupplementaryOutputPath( |
| llvm::function_ref<const std::string &(const SupplementaryOutputPaths &)> |
| extractorFn) const { |
| return forEachInputProducingSupplementaryOutput([&](const InputFile &input) |
| -> bool { |
| return !extractorFn(input.getPrimarySpecificPaths().SupplementaryOutputs) |
| .empty(); |
| }); |
| } |
| |
| bool FrontendInputsAndOutputs::hasDependenciesPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.DependenciesFilePath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasReferenceDependenciesPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.ReferenceDependenciesFilePath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasSwiftRangesPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.SwiftRangesFilePath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasCompiledSourcePath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.CompiledSourceFilePath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasObjCHeaderOutputPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.ObjCHeaderOutputPath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasLoadedModuleTracePath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.LoadedModuleTracePath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasModuleOutputPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.ModuleOutputPath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasModuleDocOutputPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.ModuleDocOutputPath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasModuleSourceInfoOutputPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.ModuleSourceInfoOutputPath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasModuleInterfaceOutputPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.ModuleInterfaceOutputPath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasPrivateModuleInterfaceOutputPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.PrivateModuleInterfaceOutputPath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasModuleSummaryOutputPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.ModuleSummaryOutputPath; |
| }); |
| } |
| bool FrontendInputsAndOutputs::hasTBDPath() const { |
| return hasSupplementaryOutputPath( |
| [](const SupplementaryOutputPaths &outs) -> const std::string & { |
| return outs.TBDPath; |
| }); |
| } |
| |
| bool FrontendInputsAndOutputs::hasDependencyTrackerPath() const { |
| return hasDependenciesPath() || hasReferenceDependenciesPath() || |
| hasLoadedModuleTracePath(); |
| } |
| |
| const PrimarySpecificPaths & |
| FrontendInputsAndOutputs::getPrimarySpecificPathsForAtMostOnePrimary() const { |
| assertMustNotBeMoreThanOnePrimaryInputUnlessBatchModeChecksHaveBeenBypassed(); |
| static auto emptyPaths = PrimarySpecificPaths(); |
| return hasInputs() ? firstInputProducingOutput().getPrimarySpecificPaths() |
| : emptyPaths; |
| } |
| |
| const PrimarySpecificPaths & |
| FrontendInputsAndOutputs::getPrimarySpecificPathsForPrimary( |
| StringRef filename) const { |
| const InputFile *f = primaryInputNamed(filename); |
| return f->getPrimarySpecificPaths(); |
| } |
| |
| const InputFile * |
| FrontendInputsAndOutputs::primaryInputNamed(StringRef name) const { |
| assert(!name.empty() && "input files have names"); |
| StringRef correctedFile = |
| InputFile::convertBufferNameFromLLVM_getFileOrSTDIN_toSwiftConventions( |
| name); |
| auto iterator = PrimaryInputsByName.find(correctedFile); |
| if (iterator == PrimaryInputsByName.end()) |
| return nullptr; |
| const InputFile *f = &AllInputs[iterator->second]; |
| assert(f->isPrimary() && "PrimaryInputsByName should only include primries"); |
| return f; |
| } |