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.
This commit is contained in:
Jon Meow
2022-04-14 10:53:46 -07:00
committed by GitHub
parent 06174c51f4
commit 0948582023
9 changed files with 307 additions and 52 deletions
+12 -2
View File
@@ -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"],
+31 -50
View File
@@ -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 {
auto main(int argc, char* argv[]) -> int {
namespace cl = llvm::cl;
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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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."