/*
* Copyright 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/Utils.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/CommandLine.h"
#include "rs_cc_options.h"
#include "slang.h"
#include "slang_assert.h"
#include <cstdlib>
#include <string>
#include <utility>
#include <vector>
enum {
OPT_INVALID = 0, // This is not an option ID.
#define PREFIX(NAME, VALUE)
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
OPT_##ID,
#include "RSCCOptions.inc"
LastOption
#undef OPTION
#undef PREFIX
};
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR)
#include "RSCCOptions.inc"
#undef OPTION
#undef PREFIX
static const llvm::opt::OptTable::Info RSCCInfoTable[] = {
#define PREFIX(NAME, VALUE)
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ \
PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS \
} \
,
#include "RSCCOptions.inc"
#undef OPTION
#undef PREFIX
};
namespace {
class RSCCOptTable : public llvm::opt::OptTable {
public:
RSCCOptTable()
: OptTable(RSCCInfoTable,
sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {}
};
}
namespace slang {
llvm::opt::OptTable *createRSCCOptTable() { return new RSCCOptTable(); }
// This function is similar to
// clang/lib/Frontend/CompilerInvocation::CreateFromArgs.
bool ParseArguments(const llvm::ArrayRef<const char *> &ArgsIn,
llvm::SmallVectorImpl<const char *> &Inputs,
RSCCOptions &Opts, clang::DiagnosticOptions &DiagOpts,
llvm::cl::StringSaver &StringSaver) {
// We use a different diagnostic engine for argument parsing from the rest of
// the work. This mimics what's done in clang. I believe it is so the
// argument parsing errors are well formatted while the full errors can be
// influenced by command line arguments.
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> ArgumentParseDiagOpts(
new clang::DiagnosticOptions());
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
new clang::DiagnosticIDs());
DiagnosticBuffer DiagsBuffer;
clang::DiagnosticsEngine DiagEngine(DiagIDs, &*ArgumentParseDiagOpts,
&DiagsBuffer, false);
// Populate a vector with the command line arguments, expanding command files
// that have been included via the '@' argument.
llvm::SmallVector<const char *, 256> ArgVector;
ArgVector.append(ArgsIn.begin(), ArgsIn.end());
llvm::cl::ExpandResponseFiles(StringSaver, llvm::cl::TokenizeGNUCommandLine,
ArgVector, false);
std::unique_ptr<llvm::opt::OptTable> OptParser(createRSCCOptTable());
unsigned MissingArgIndex = 0;
unsigned MissingArgCount = 0;
std::unique_ptr<llvm::opt::InputArgList> Args(
OptParser->ParseArgs(ArgVector.begin() + 1, ArgVector.end(),
MissingArgIndex, MissingArgCount));
// Check for missing argument error.
if (MissingArgCount) {
DiagEngine.Report(clang::diag::err_drv_missing_argument)
<< Args->getArgString(MissingArgIndex) << MissingArgCount;
}
// Issue errors on unknown arguments.
for (llvm::opt::arg_iterator it = Args->filtered_begin(OPT_UNKNOWN),
ie = Args->filtered_end();
it != ie; ++it) {
DiagEngine.Report(clang::diag::err_drv_unknown_argument)
<< (*it)->getAsString(*Args);
}
DiagOpts.IgnoreWarnings = Args->hasArg(OPT_w);
DiagOpts.Warnings = Args->getAllArgValues(OPT_W);
for (llvm::opt::ArgList::const_iterator it = Args->begin(), ie = Args->end();
it != ie; ++it) {
const llvm::opt::Arg *A = *it;
if (A->getOption().getKind() == llvm::opt::Option::InputClass)
Inputs.push_back(A->getValue());
}
Opts.mIncludePaths = Args->getAllArgValues(OPT_I);
Opts.mBitcodeOutputDir = Args->getLastArgValue(OPT_o);
if (const llvm::opt::Arg *A = Args->getLastArg(OPT_M_Group)) {
switch (A->getOption().getID()) {
case OPT_M: {
Opts.mEmitDependency = true;
Opts.mOutputType = Slang::OT_Dependency;
break;
}
case OPT_MD: {
Opts.mEmitDependency = true;
Opts.mOutputType = Slang::OT_Bitcode;
break;
}
default: { slangAssert(false && "Invalid option in M group!"); }
}
}
if (const llvm::opt::Arg *A = Args->getLastArg(OPT_Output_Type_Group)) {
switch (A->getOption().getID()) {
case OPT_emit_asm: {
Opts.mOutputType = Slang::OT_Assembly;
break;
}
case OPT_emit_llvm: {
Opts.mOutputType = Slang::OT_LLVMAssembly;
break;
}
case OPT_emit_bc: {
Opts.mOutputType = Slang::OT_Bitcode;
break;
}
case OPT_emit_nothing: {
Opts.mOutputType = Slang::OT_Nothing;
break;
}
default: { slangAssert(false && "Invalid option in output type group!"); }
}
}
if (Opts.mEmitDependency && ((Opts.mOutputType != Slang::OT_Bitcode) &&
(Opts.mOutputType != Slang::OT_Dependency)))
DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
<< Args->getLastArg(OPT_M_Group)->getAsString(*Args)
<< Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args);
Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix);
Opts.mJavaReflectionPathBase =
Args->getLastArgValue(OPT_java_reflection_path_base);
Opts.mJavaReflectionPackageName =
Args->getLastArgValue(OPT_java_reflection_package_name);
Opts.mRSPackageName = Args->getLastArgValue(OPT_rs_package_name);
llvm::StringRef BitcodeStorageValue =
Args->getLastArgValue(OPT_bitcode_storage);
if (BitcodeStorageValue == "ar")
Opts.mBitcodeStorage = BCST_APK_RESOURCE;
else if (BitcodeStorageValue == "jc")
Opts.mBitcodeStorage = BCST_JAVA_CODE;
else if (!BitcodeStorageValue.empty())
DiagEngine.Report(clang::diag::err_drv_invalid_value)
<< OptParser->getOptionName(OPT_bitcode_storage) << BitcodeStorageValue;
llvm::opt::Arg *lastBitwidthArg = Args->getLastArg(OPT_m32, OPT_m64);
if (Args->hasArg(OPT_reflect_cpp)) {
Opts.mBitcodeStorage = BCST_CPP_CODE;
// mJavaReflectionPathBase can be set for C++ reflected builds.
// Set it to the standard mBitcodeOutputDir (via -o) by default.
if (Opts.mJavaReflectionPathBase.empty()) {
Opts.mJavaReflectionPathBase = Opts.mBitcodeOutputDir;
}
// Check for bitwidth arguments.
if (lastBitwidthArg) {
if (lastBitwidthArg->getOption().matches(OPT_m32)) {
Opts.mBitWidth = 32;
} else {
Opts.mBitWidth = 64;
}
}
} else if (lastBitwidthArg) {
// -m32/-m64 are forbidden for non-C++ reflection paths.
DiagEngine.Report(
DiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
"cannot use -m32/-m64 without specifying "
"C++ reflection (-reflect-c++)"));
}
Opts.mDependencyOutputDir =
Args->getLastArgValue(OPT_output_dep_dir, Opts.mBitcodeOutputDir);
Opts.mAdditionalDepTargets = Args->getAllArgValues(OPT_additional_dep_target);
Opts.mShowHelp = Args->hasArg(OPT_help);
Opts.mShowVersion = Args->hasArg(OPT_version);
Opts.mDebugEmission = Args->hasArg(OPT_emit_g);
Opts.mVerbose = Args->hasArg(OPT_verbose);
// If we are emitting both 32-bit and 64-bit bitcode, we must embed it.
size_t OptLevel =
clang::getLastArgIntValue(*Args, OPT_optimization_level, 3, DiagEngine);
Opts.mOptimizationLevel =
OptLevel == 0 ? llvm::CodeGenOpt::None : llvm::CodeGenOpt::Aggressive;
Opts.mTargetAPI =
clang::getLastArgIntValue(*Args, OPT_target_api, RS_VERSION, DiagEngine);
if (Opts.mTargetAPI == 0) {
Opts.mTargetAPI = UINT_MAX;
}
Opts.mEmit3264 =
(Opts.mTargetAPI >= 21) && (Opts.mBitcodeStorage != BCST_CPP_CODE);
if (Opts.mEmit3264) {
Opts.mBitcodeStorage = BCST_JAVA_CODE;
}
if (DiagEngine.hasErrorOccurred()) {
llvm::errs() << DiagsBuffer.str();
return false;
}
return true;
}
}