// Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 #include "log.hh" #include "pipeline.hh" #include "platform.hh" #include "serializer.hh" #include "test.hh" #include "working_files.hh" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ccls; using namespace llvm; using namespace llvm::cl; namespace ccls { std::vector g_init_options; } namespace { OptionCategory C("ccls options"); opt opt_help("h", desc("Alias for -help"), cat(C)); opt opt_verbose("v", desc("verbosity, from -3 (fatal) to 2 (verbose)"), init(0), cat(C)); opt opt_test_index("test-index", ValueOptional, init("!"), desc("run index tests"), cat(C)); opt opt_index("index", desc("standalone mode: index a project and exit"), value_desc("root"), cat(C)); list opt_init("init", desc("extra initialization options in JSON"), cat(C)); opt opt_log_file("log-file", desc("stderr or log file"), value_desc("file"), init("stderr"), cat(C)); opt opt_log_file_append("log-file-append", desc("append to log file"), cat(C)); void CloseLog() { fclose(ccls::log::file); } } // namespace int main(int argc, char **argv) { TraceMe(); sys::PrintStackTraceOnErrorSignal(argv[0]); cl::SetVersionPrinter([](raw_ostream &OS) { OS << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang") << "\n"; }); for (auto &I : TopLevelSubCommand->OptionsMap) #if LLVM_VERSION_MAJOR >= 9 // rL360179 if (I.second->Categories[0] != &C) #else if (I.second->Category != &C) #endif I.second->setHiddenFlag(ReallyHidden); ParseCommandLineOptions(argc, argv, "C/C++/Objective-C language server\n\n" "See more on https://github.com/MaskRay/ccls/wiki"); if (opt_help) { PrintHelpMessage(); return 0; } ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue()); pipeline::Init(); const char *env = getenv("CCLS_CRASH_RECOVERY"); if (!env || strcmp(env, "0") != 0) CrashRecoveryContext::Enable(); bool language_server = true; if (opt_log_file.size()) { ccls::log::file = opt_log_file == "stderr" ? stderr : fopen(opt_log_file.c_str(), opt_log_file_append ? "ab" : "wb"); if (!ccls::log::file) { fprintf(stderr, "failed to open %s\n", opt_log_file.c_str()); return 2; } setbuf(ccls::log::file, NULL); atexit(CloseLog); } if (opt_test_index != "!") { language_server = false; if (!ccls::RunIndexTests(opt_test_index, sys::Process::StandardInIsUserInput())) return 1; } if (language_server) { if (!opt_init.empty()) { // We check syntax error here but override client-side // initializationOptions in messages/initialize.cc g_init_options = opt_init; rapidjson::Document reader; for (const std::string &str : g_init_options) { rapidjson::ParseResult ok = reader.Parse(str.c_str()); if (!ok) { fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n", rapidjson::GetParseError_En(ok.Code()), ok.Offset()); return 1; } JsonReader json_reader{&reader}; try { Config config; Reflect(json_reader, config); } catch (std::invalid_argument &e) { fprintf(stderr, "Failed to parse --init %s, expected %s\n", static_cast(json_reader).GetPath().c_str(), e.what()); return 1; } } } sys::ChangeStdinToBinary(); sys::ChangeStdoutToBinary(); if (opt_index.size()) { SmallString<256> Root(opt_index); sys::fs::make_absolute(Root); pipeline::Standalone(Root.str()); } else { // The thread that reads from stdin and dispatchs commands to the main // thread. pipeline::LaunchStdin(); // The thread that writes responses from the main thread to stdout. pipeline::LaunchStdout(); // Main thread which also spawns indexer threads upon the "initialize" // request. pipeline::MainLoop(); } } return 0; }