Przeglądaj źródła

Add install scripts (#1186)

I think this will work on linux/mac, not windows obviously. But the essence is that running `bazel run -c opt //installers/local:install` will create:

Symlink: /usr/bin/carbon-explorer -> /usr/lib/carbon/carbon
BusyBox-style binary: /usr/lib/carbon/carbon
File: /usr/lib/carbon/data/prelude.carbon

And then just running `carbon-explorer foo.carbon` with no flags (in particular, not --prelude) will work.

Since this removes the need for prelude_file in most cases, I'm removing the relative path logic added in #1179 -- it would otherwise conflict with what I'm doing here. I *think* overall this will result in something even simpler and more reliable for compiler explorer to use, and hopefully with enough flexibility that it's easy for us to change how it's implemented later without breaking much.
Jon Meow 4 lat temu
rodzic
commit
0948582023

+ 12 - 2
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"],

+ 30 - 49
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 <unistd.h>
 
 #include <cstdio>
@@ -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 {
+
+namespace cl = llvm::cl;
 
-auto main(int argc, char* argv[]) -> int {
+static auto Main(llvm::StringRef default_prelude_file, int argc, char* argv[])
+    -> ErrorOr<Success> {
   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<bool> trace_option("trace", desc("Enable tracing"));
-  opt<std::string> input_file_name(llvm::cl::Positional, desc("<input file>"),
-                                   llvm::cl::Required);
+  cl::opt<bool> trace_option("trace", cl::desc("Enable tracing"));
+  cl::opt<std::string> input_file_name(cl::Positional, cl::desc("<input file>"),
+                                       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<std::string> 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<std::string> prelude_file_name("prelude", desc("<prelude file>"),
-                                     llvm::cl::init(prelude_path.str().str()));
+  cl::opt<std::string> prelude_file_name("prelude", cl::desc("<prelude file>"),
+                                         cl::init(default_prelude_file.str()));
+  cl::ParseCommandLineOptions(argc, argv);
 
-  llvm::cl::ParseCommandLineOptions(argc, argv);
-
-  Carbon::Arena arena;
-  Carbon::ErrorOr<Carbon::AST> 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<int> 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

+ 18 - 0
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_

+ 11 - 0
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);
+}

+ 67 - 0
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",
+    ],
+)

+ 27 - 0
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<void*>(&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;
+  }
+}

+ 18 - 0
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),
+)

+ 68 - 0
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 <<EOF
+  # Ensure directories exist.
+  mkdir -p "${INSTALL_PATH}/bin"
+  mkdir -p "${INSTALL_PATH}/lib/carbon/data"
+
+  # Install files to lib.
+  install -m 755 "${CARBON}" "${INSTALL_PATH}/lib/carbon/carbon"
+  for f in $(printf " %q" "${STANDARD_LIBRARIES[@]}"); do
+    install -m 644 "\${f}" "${INSTALL_PATH}/lib/carbon/data/"
+  done
+
+  # Add symlinks in bin.
+  ln -fs "${INSTALL_PATH}/lib/carbon/carbon" \
+    "${INSTALL_PATH}/bin/carbon-explorer"
+EOF
+)
+
+# Only use sudo if the target directory isn't user-owned.
+ACCESS_PATH="${INSTALL_PATH}"
+while [[ ! -e "${ACCESS_PATH}" ]]; do
+  ACCESS_PATH="$(dirname "${ACCESS_PATH}")"
+done
+
+if [[ -O "${ACCESS_PATH}" ]]; then
+  echo "Installing files..."
+  echo "${SCRIPT}" | /usr/bin/bash -eux -
+else
+  echo "Installing files using sudo..."
+  echo "${SCRIPT}" | sudo -- /usr/bin/bash -eux -
+fi
+echo "All done."

+ 55 - 0
installers/local/uninstall.sh

@@ -0,0 +1,55 @@
+#!/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
+#
+# Removes files added by install.sh.
+
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    --install_path)
+      INSTALL_PATH="$2"
+      shift
+      shift
+      ;;
+    *)
+      echo "Unexpected argument: $1"
+      exit 1
+      ;;
+  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 uninstall script to run.
+# TODO: As more files are added, consider sharing better with install.sh. Maybe
+# still keep deleting legacy (no longer installed) files.
+SCRIPT=$(cat <<EOF
+  # Clean up deliberately installed files.
+  rm -f "${INSTALL_PATH}/bin/carbon-explorer"
+  rm -rf "${INSTALL_PATH}/lib/carbon"
+
+  # Clean up higher level directories in case we created them.
+  rmdir -p "${INSTALL_PATH}/bin" || true
+  rmdir -p "${INSTALL_PATH}/lib" || true
+EOF
+)
+
+# Only use sudo if the target directory isn't user-owned.
+ACCESS_PATH="${INSTALL_PATH}"
+while [[ ! -e "${ACCESS_PATH}" ]]; do
+  ACCESS_PATH="$(dirname "${ACCESS_PATH}")"
+done
+
+if [[ -O "${ACCESS_PATH}" ]]; then
+  echo "Uninstalling files..."
+  echo "${SCRIPT}" | /usr/bin/bash -eux -
+else
+  echo "Uninstalling files using sudo..."
+  echo "${SCRIPT}" | sudo -- /usr/bin/bash -eux -
+fi
+echo "All done."