diff --git a/executable_semantics/BUILD b/executable_semantics/BUILD index 1405b195d14e..b2f594e1d3f3 100644 --- a/executable_semantics/BUILD +++ b/executable_semantics/BUILD @@ -7,6 +7,7 @@ load("@mypy_integration//:mypy.bzl", "mypy_test") package(default_visibility = [ "//bazel/check_deps:__pkg__", "//executable_semantics:__subpackages__", + "//installers:__subpackages__", ]) filegroup( @@ -14,9 +15,10 @@ filegroup( srcs = ["data/prelude.carbon"], ) -cc_binary( - name = "executable_semantics", +cc_library( + name = "main", srcs = ["main.cpp"], + hdrs = ["main.h"], deps = [ "//common:error", "//executable_semantics/common:arena", @@ -28,6 +30,14 @@ cc_binary( ], ) +cc_binary( + name = "executable_semantics", + srcs = ["main_bin.cpp"], + deps = [ + ":main", + ], +) + py_binary( name = "gen_rtti", srcs = ["gen_rtti.py"], diff --git a/executable_semantics/main.cpp b/executable_semantics/main.cpp index 00a001b61af3..7a8cecc8e7ad 100644 --- a/executable_semantics/main.cpp +++ b/executable_semantics/main.cpp @@ -2,6 +2,8 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include "executable_semantics/main.h" + #include #include @@ -16,21 +18,15 @@ #include "executable_semantics/interpreter/exec_program.h" #include "executable_semantics/syntax/parse.h" #include "executable_semantics/syntax/prelude.h" -#include "llvm/ADT/SmallString.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Program.h" -// Prints an error message and returns error code value. -auto PrintError(const Carbon::Error& error) -> int { - llvm::errs() << error.message() << "\n"; - return EXIT_FAILURE; -} +namespace Carbon { -auto main(int argc, char* argv[]) -> int { +namespace cl = llvm::cl; + +static auto Main(llvm::StringRef default_prelude_file, int argc, char* argv[]) + -> ErrorOr { llvm::setBugReportMsg( "Please report issues to " "https://github.com/carbon-language/carbon-lang/issues and include the " @@ -41,48 +37,33 @@ auto main(int argc, char* argv[]) -> int { // is piped to stdout. llvm::errs().tie(&llvm::outs()); - using llvm::cl::desc; - using llvm::cl::opt; - opt trace_option("trace", desc("Enable tracing")); - opt input_file_name(llvm::cl::Positional, desc(""), - llvm::cl::Required); + cl::opt trace_option("trace", cl::desc("Enable tracing")); + cl::opt input_file_name(cl::Positional, cl::desc(""), + cl::Required); // Find the path of the executable if possible and use that as a relative root - // for finding the prelude. FIXME: Currently, this assumes a Bazel-like - // runfiles tree rather than any kind of installation tree. - llvm::SmallString<256> exe_path(argv[0]); - if (!llvm::sys::fs::exists(exe_path)) { - // Try to lookup the program name in the `PATH`. - if (llvm::ErrorOr path = - llvm::sys::findProgramByName(exe_path)) { - exe_path = *path; - } - } - // If we have a valid path, find the parent directory. Otherwise, just use an - // empty string to get the current working directory. - if (llvm::sys::fs::exists(exe_path)) { - llvm::sys::path::remove_filename(exe_path); - } else { - exe_path = ""; - } - llvm::SmallString<256> prelude_path = exe_path; - llvm::sys::path::append(prelude_path, "data/prelude.carbon"); - opt prelude_file_name("prelude", desc(""), - llvm::cl::init(prelude_path.str().str())); + cl::opt prelude_file_name("prelude", cl::desc(""), + cl::init(default_prelude_file.str())); + cl::ParseCommandLineOptions(argc, argv); - llvm::cl::ParseCommandLineOptions(argc, argv); - - Carbon::Arena arena; - Carbon::ErrorOr ast = - Carbon::Parse(&arena, input_file_name, trace_option); - if (!ast.ok()) { - return PrintError(ast.error()); - } - AddPrelude(prelude_file_name, &arena, &ast->declarations); + Arena arena; + ASSIGN_OR_RETURN(AST ast, Parse(&arena, input_file_name, trace_option)); + AddPrelude(prelude_file_name, &arena, &ast.declarations); // Typecheck and run the parsed program. - Carbon::ErrorOr result = Carbon::ExecProgram(&arena, *ast, trace_option); - if (!result.ok()) { - return PrintError(result.error()); - } + ASSIGN_OR_RETURN(int unused_return_code, + ExecProgram(&arena, ast, trace_option)); + (void)unused_return_code; + return Success(); } + +auto ExecutableSemanticsMain(llvm::StringRef default_prelude_file, int argc, + char** argv) -> int { + if (auto result = Main(default_prelude_file, argc, argv); !result.ok()) { + llvm::errs() << result.error().message() << "\n"; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +} // namespace Carbon diff --git a/executable_semantics/main.h b/executable_semantics/main.h new file mode 100644 index 000000000000..594f1e289319 --- /dev/null +++ b/executable_semantics/main.h @@ -0,0 +1,18 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_MAIN_H_ +#define EXECUTABLE_SEMANTICS_MAIN_H_ + +#include "llvm/ADT/StringRef.h" + +namespace Carbon { + +// Runs executable semantics. +auto ExecutableSemanticsMain(llvm::StringRef default_prelude_file, int argc, + char** argv) -> int; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_MAIN_H_ diff --git a/executable_semantics/main_bin.cpp b/executable_semantics/main_bin.cpp new file mode 100644 index 000000000000..11a3a4d9f427 --- /dev/null +++ b/executable_semantics/main_bin.cpp @@ -0,0 +1,11 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "executable_semantics/main.h" + +auto main(int argc, char** argv) -> int { + // This assumes execution from bazel, in runfiles. + return Carbon::ExecutableSemanticsMain( + "executable_semantics/data/prelude.carbon", argc, argv); +} diff --git a/installers/local/BUILD b/installers/local/BUILD new file mode 100644 index 000000000000..1c0d226950d0 --- /dev/null +++ b/installers/local/BUILD @@ -0,0 +1,67 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# Installs Carbon to /usr/bin and /usr/lib/carbon. +# +# To install to /usr, run: +# bazel run -c opt //installers/local:install +# +# To use a custom install path, run: +# bazel run -c opt //installers/local:install +# --//installers/local:install_path=/my/path +# +# To uninstall, run: +# bazel //installers/local:uninstall +# +# The build mode is important for installs, as debug versions may be installed. +# It is not relevant for uninstalls. +# +# If a custom install path is passed to :install, the same should be passed to +# :uninstall. + +load("install.bzl", "install_path_rule") + +# Turns `--//installers/local:install_path=arg` into `$(INSTALL_PATH)`. +install_path_rule( + name = "install_path", + build_setting_default = "/usr", +) + +sh_binary( + name = "install", + srcs = ["install.sh"], + args = [ + "--install_path", + "$(INSTALL_PATH)", + "--carbon", + "$(location :carbon)", + "$(locations //executable_semantics:standard_libraries)", + ], + data = [ + ":carbon", + "//executable_semantics:standard_libraries", + ], + toolchains = [":install_path"], +) + +sh_binary( + name = "uninstall", + srcs = ["uninstall.sh"], + args = [ + "--install_path", + "$(INSTALL_PATH)", + ], + toolchains = [":install_path"], +) + +# A busybox-like binary for Carbon tools; multiple tools are part of this binary +# so that shared code is deduplicated. +cc_binary( + name = "carbon", + srcs = ["carbon.cpp"], + deps = [ + "//executable_semantics:main", + "@llvm-project//llvm:Support", + ], +) diff --git a/installers/local/carbon.cpp b/installers/local/carbon.cpp new file mode 100644 index 000000000000..797da4287b36 --- /dev/null +++ b/installers/local/carbon.cpp @@ -0,0 +1,27 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "executable_semantics/main.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +namespace fs = llvm::sys::fs; +namespace path = llvm::sys::path; + +auto main(int argc, char** argv) -> int { + llvm::StringRef bin = path::filename(argv[0]); + if (bin == "carbon-explorer") { + static int static_for_main_addr; + std::string exe = fs::getMainExecutable( + argv[0], static_cast(&static_for_main_addr)); + llvm::StringRef install_path = path::parent_path(exe); + llvm::SmallString<256> prelude_file(install_path); + path::append(prelude_file, "data", "prelude.carbon"); + return Carbon::ExecutableSemanticsMain(prelude_file, argc, argv); + } else { + fprintf(stderr, "Unrecognized Carbon binary requested: %s", argv[0]); + return 1; + } +} diff --git a/installers/local/install.bzl b/installers/local/install.bzl new file mode 100644 index 000000000000..96e02cb22035 --- /dev/null +++ b/installers/local/install.bzl @@ -0,0 +1,18 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""Install-related rules.""" + +def _install_path_rule_impl(ctx): + """Turns the install path into a variable for rules to use.""" + return [ + platform_common.TemplateVariableInfo({ + "INSTALL_PATH": ctx.build_setting_value, + }), + ] + +install_path_rule = rule( + implementation = _install_path_rule_impl, + build_setting = config.string(flag = True), +) diff --git a/installers/local/install.sh b/installers/local/install.sh new file mode 100755 index 000000000000..0d993516335f --- /dev/null +++ b/installers/local/install.sh @@ -0,0 +1,68 @@ +#!/bin/bash -eu +# +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# Performs a local install. This will replace any previous carbon installs +# without prompting. + +STANDARD_LIBRARIES=() + +while [[ $# -gt 0 ]]; do + case "$1" in + --carbon) + CARBON="$2" + shift + shift + ;; + --install_path) + INSTALL_PATH="$2" + shift + shift + ;; + *) + STANDARD_LIBRARIES+=("$1") + echo $1 + shift + ;; + esac +done + +# If the install path is relative, change it to be based on the working dir. +if [[ ! "${INSTALL_PATH}" = /* ]]; then + INSTALL_PATH="${BUILD_WORKING_DIRECTORY}/${INSTALL_PATH}" +fi + +# Prepare the install script to run. +SCRIPT=$(cat <