diff --git a/compiler/main/driver.cpp b/compiler/main/driver.cpp index 611e72fd4915..d3e92b1abd25 100644 --- a/compiler/main/driver.cpp +++ b/compiler/main/driver.cpp @@ -345,6 +345,7 @@ static bool fPrintLicense = false; static bool fPrintSettingsHelp = false; static bool fPrintVersion = false; static bool fPrintChplHome = false; +static bool fPrintBootstrapCommands = false; std::string llvmFlags; std::string llvmRemarksFilters; @@ -409,12 +410,6 @@ std::vector gDynoGenLibSourcePaths; // what top-level module names as astrs were requested to be stored in the lib? std::unordered_set gDynoGenLibModuleNameAstrs; -static bool isMaybeChplHome(const char* path) -{ - return chpl::isMaybeChplHome(std::string(path)); - -} - static void setChplHomeDerivedVars() { int rc; rc = snprintf(CHPL_RUNTIME_LIB, FILENAME_MAX, "%s/%s", @@ -470,112 +465,35 @@ static bool restoreChplHomeDerivedFromEnv() { return haveAll; } +int main(int argc, char* argv[]); + static void setupChplHome(const char* argv0) { - const char* chpl_home = getenv("CHPL_HOME"); - char* guess = NULL; - bool installed = false; char majMinorVers[64]; + std::string foundChplHome; + bool installed = false; + bool fromEnv = false; + std::string diagnosticMsg; // Get major.minor version string (used below) get_major_minor_version(majMinorVers, sizeof(majMinorVers)); - // Get the executable path. - guess = findProgramPath(argv0); - - if (guess) { - // Determine CHPL_HOME based on the exe path. - // Determined exe path, but don't have a env var set - // Look for ../../../util/chplenv - // Remove the /bin/some-platform/chpl part - // from the path. - if( guess[0] ) { - int j = strlen(guess) - 5; // /bin and '\0' - for( ; j >= 0; j-- ) { - if( guess[j] == '/' && - guess[j+1] == 'b' && - guess[j+2] == 'i' && - guess[j+3] == 'n' ) { - guess[j] = '\0'; - break; - } - } - } + auto err = chpl::findChplHome(argv0, (void*) main, foundChplHome, + installed, fromEnv, diagnosticMsg); - if( isMaybeChplHome(guess) ) { - // OK! + if (!diagnosticMsg.empty()) { + if (err) { + USR_FATAL("%s\n", diagnosticMsg.c_str()); } else { - // Maybe we are in e.g. /usr/bin. - free(guess); - guess = NULL; + USR_WARN("%s\n", diagnosticMsg.c_str()); } + } else if (err) { + USR_FATAL("$CHPL_HOME must be set to run chpl"); } - if( chpl_home ) { - if( strlen(chpl_home) > FILENAME_MAX ) - USR_FATAL("$CHPL_HOME=%s path too long", chpl_home); - - if( guess == NULL ) { - // Could not find exe path, but have a env var set - } else { - // We have env var and found exe path. - // Check that they match and emit a warning if not. - if( ! isSameFile(chpl_home, guess) ) { - // Not the same. Emit warning. - USR_WARN("$CHPL_HOME=%s mismatched with executable home=%s", - chpl_home, guess); - } - } - // Since we have an enviro var, always use that. - strncpy(CHPL_HOME, chpl_home, FILENAME_MAX); - } else { - - // Check in a default location too - if( guess == NULL ) { - char TEST_HOME[FILENAME_MAX+1] = ""; - - // Check for Chapel libraries at installed prefix - // e.g. /usr/share/chapel/ - int rc; - rc = snprintf(TEST_HOME, FILENAME_MAX, "%s/%s/%s", - get_configured_prefix(), // e.g. /usr - "share/chapel", - majMinorVers); - if ( rc >= FILENAME_MAX ) USR_FATAL("Installed pathname too long"); - - if( isMaybeChplHome(TEST_HOME) ) { - guess = strdup(TEST_HOME); - - installed = true; - } - } - - if( guess == NULL ) { - // Could not find enviro var, and could not - // guess at exe's path name. - USR_FATAL("$CHPL_HOME must be set to run chpl"); - } else { - int rc; - - if( strlen(guess) > FILENAME_MAX ) - USR_FATAL("chpl guessed home %s too long", guess); - - // Determined exe path, but don't have a env var set - strncpy(CHPL_HOME, guess, FILENAME_MAX); - // Also need to setenv in this case. - rc = setenv("CHPL_HOME", guess, 0); - if( rc ) USR_FATAL("Could not setenv CHPL_HOME"); - } - } - - // Check that the resulting path is a Chapel distribution. - if( ! isMaybeChplHome(CHPL_HOME) ) { - // Bad enviro var. - USR_WARN("CHPL_HOME=%s is not a Chapel distribution", CHPL_HOME); + if (foundChplHome.size() > FILENAME_MAX) { + USR_FATAL("$CHPL_HOME=%s path too long", foundChplHome.c_str()); } - - if( guess ) - free(guess); - + strncpy(CHPL_HOME, foundChplHome.c_str(), FILENAME_MAX); // Get derived-from-home vars @@ -1472,6 +1390,7 @@ static ArgumentDescription arg_desc[] = { {"parse-only", ' ', NULL, "Stop compiling after 'parse' pass for syntax checking", "N", &fParseOnly, NULL, NULL}, {"parser-debug", ' ', NULL, "Set parser debug level", "+", &debugParserLevel, "CHPL_PARSER_DEBUG", NULL}, {"debug-short-loc", ' ', NULL, "Display long [short] location in certain debug outputs", "N", &debugShortLoc, "CHPL_DEBUG_SHORT_LOC", NULL}, + {"print-bootstrap-commands", ' ', NULL, "Print a Bash bootstrap script to be executed by scripts to determine necessary environment variables.", "F", &fPrintBootstrapCommands, NULL,NULL}, {"print-emitted-code-size", ' ', NULL, "Print emitted code size", "F", &fPrintEmittedCodeSize, NULL, NULL}, {"print-module-resolution", ' ', NULL, "Print name of module being resolved", "F", &fPrintModuleResolution, "CHPL_PRINT_MODULE_RESOLUTION", NULL}, {"print-dispatch", ' ', NULL, "Print dynamic dispatch table", "F", &fPrintDispatch, NULL, NULL}, @@ -1674,6 +1593,11 @@ static void printStuff(const char* argv0) { printf("%s\n", CHPL_HOME); printedSomething = true; } + if ( fPrintBootstrapCommands ) { + printf("export CHPL_HOME='%s'\n", CHPL_HOME); + printf("export CHPL_THIRD_PARTY='%s'\n", CHPL_THIRD_PARTY); + printedSomething = true; + } if ( fPrintChplLoc ) { char* guess = findProgramPath(argv0); diff --git a/frontend/include/chpl/util/chplenv.h b/frontend/include/chpl/util/chplenv.h index 7dafe7b6ddea..964cdd9ef846 100644 --- a/frontend/include/chpl/util/chplenv.h +++ b/frontend/include/chpl/util/chplenv.h @@ -67,15 +67,14 @@ bool isMaybeChplHome(std::string path); /* Try to locate a proper CHPL_HOME value given the `main` executable's name and memory address. Output variables chplHomeOut, installed, fromEnv, and - warningMessage are used to capture probable CHPL_HOME, whether chpl appears + diagnosticMessage are used to capture probable CHPL_HOME, whether chpl appears to be installed, whether we got the value of CHPL_HOME from the environment var, - and a possible warning message if CHPL_HOME and the chpl executable's location - do not match. + and a possible diagnostic message if the function needs to report an issue. */ -std::error_code findChplHome(char* argv0, void* mainAddr, +std::error_code findChplHome(const char* argv0, void* mainAddr, std::string& chplHomeOut, bool& installed, bool& fromEnv, - std::string& warningMessage); + std::string& diagnosticMessage); } // namespace chpl diff --git a/frontend/lib/util/chplenv.cpp b/frontend/lib/util/chplenv.cpp index 7fe56aabb1f7..fa415e5ed63b 100644 --- a/frontend/lib/util/chplenv.cpp +++ b/frontend/lib/util/chplenv.cpp @@ -119,112 +119,89 @@ bool isMaybeChplHome(std::string path) { return llvm::sys::fs::exists(path); } -std::error_code findChplHome(char* argv0, void* mainAddr, +std::error_code findChplHome(const char* argv0, void* mainAddr, std::string& chplHomeOut, bool& installed, bool& fromEnv, - std::string& warningMessage) { + std::string& diagnosticMessage) { std::string versionString = getMajorMinorVersion(); - std::string guess = getExecutablePath(argv0, mainAddr); + std::string guessFromBinaryPath = getExecutablePath(argv0, mainAddr); + chplHomeOut = std::string(); - const char* chpl_home = getenv("CHPL_HOME"); + const char* chplHomeEnv = getenv("CHPL_HOME"); - if (!guess.empty()) { + // First, Try figuring CHPL_HOME out from the binary's location. + // If we're running from /path/to/folder/bin/darwin/chpl, + // CHPL_HOME might be /path/to/folder. + if (!guessFromBinaryPath.empty()) { // truncate path at /bin - char* tmp_guess = strdup(guess.c_str()); - if ( tmp_guess[0] ) { - int j = strlen(tmp_guess) - 5; // /bin and '\0' - for ( ; j >= 0; j-- ) { - if ( tmp_guess[j] == '/' && - tmp_guess[j+1] == 'b' && - tmp_guess[j+2] == 'i' && - tmp_guess[j+3] == 'n' ) { - tmp_guess[j] = '\0'; - break; - } - } + auto binIdx = guessFromBinaryPath.rfind("/bin"); + if (binIdx != std::string::npos) { + guessFromBinaryPath.resize(binIdx); } - guess = std::string(tmp_guess); - if (isMaybeChplHome(guess)) { - chplHomeOut = guess; + + if (isMaybeChplHome(guessFromBinaryPath)) { + chplHomeOut = guessFromBinaryPath; } else { - guess = ""; + guessFromBinaryPath.clear(); } } - if (chpl_home) { + // Compute a predefined location based on the prefix. If we find that + // the CHPL_HOME lies in this location, we can reason that this is + // a prefix-based installation, and install should be true. + // + // Check for Chapel libraries at installed prefix + // e.g. /usr/share/chapel/ + std::string guessFromPrefix = std::string() + + getConfiguredPrefix() + "/" + + "share/chapel/" + + versionString; + if (!isMaybeChplHome(guessFromPrefix)) { + guessFromPrefix.clear(); + } + + if (chplHomeEnv) { + // If the CHPL_HOME environment variable is set, that is our source of + // truth. Do some more work to compare it to other guesses and gather info. + + chplHomeOut = chplHomeEnv; fromEnv = true; - if(strlen(chpl_home) > FILENAME_MAX) - // USR_FATAL("$CHPL_HOME=%s path too long", chpl_home); - // error_state - // TODO: customize error message? - return std::make_error_code(std::errc::filename_too_long); - if (guess.empty()) { - // Could not find exe path, but have a env var set - chplHomeOut = std::string(chpl_home); - } else { - // We have env var and found exe path. - // Check that they match and emit a warning if not. - if ( ! isSameFile(chpl_home, guess.c_str()) ) { - // Not the same. Emit warning. - //USR_WARN("$CHPL_HOME=%s mismatched with executable home=%s", - // chpl_home, guess); - warningMessage = "$CHPL_HOME=" + std::string(chpl_home) + " is mismatched with executable home=" + guess; - } - // Since we have an enviro var, always use that. - chplHomeOut = std::string(chpl_home); + + if (isSameFile(chplHomeEnv, guessFromPrefix.c_str())) { + // The pre-configured prefix path matches the variable in the environment; + // this indicates that the CHPL_HOME is from a prefix-based installation. + installed = true; } - } else { - // Check in a default location too - if (guess.empty()) { - char TEST_HOME[FILENAME_MAX+1] = ""; - - // Check for Chapel libraries at installed prefix - // e.g. /usr/share/chapel/ - int rc; - rc = snprintf(TEST_HOME, FILENAME_MAX, "%s/%s/%s", - getConfiguredPrefix(), // e.g. /usr - "share/chapel", - versionString.c_str()); - if (rc >= FILENAME_MAX) { - // USR_FATAL("Installed pathname too long"); - // TODO: return an error here - return std::make_error_code(std::errc::filename_too_long); - } - - if (isMaybeChplHome(TEST_HOME)) { - guess = strdup(TEST_HOME); - installed = true; - } + + // Emit a warning if we could guess the CHPL_HOME from the binary's path, + // but it's not the same path as the environment variable. + if (!guessFromBinaryPath.empty() && + !isSameFile(chplHomeEnv, guessFromBinaryPath.c_str())) { + diagnosticMessage = "$CHPL_HOME=" + std::string(chplHomeEnv) + + " is mismatched with executable home=" + + guessFromBinaryPath; } + } else if (guessFromBinaryPath.empty() && !guessFromPrefix.empty()) { + // If no environment variable set, and the path-based guess failed, + // the last resort is a prefix-based guess; in this case, installed = true. - if (guess.empty()) { - // Could not find enviro var, and could not - // guess at exe's path name. - // USR_FATAL("$CHPL_HOME must be set to run chpl"); - // TODO: customize the error message - return std::make_error_code(std::errc::no_such_file_or_directory); - } else { - int rc; - - if (guess.length() > FILENAME_MAX) { - // USR_FATAL("chpl guessed home %s too long", guess); - return std::make_error_code(std::errc::filename_too_long); - } - // Determined exe path, but don't have a env var set - rc = setenv("CHPL_HOME", guess.c_str(), 0); - if ( rc ) { - // USR_FATAL("Could not setenv CHPL_HOME"); - // TODO: customize the error message - return std::make_error_code(std::errc::no_such_file_or_directory); - } - chplHomeOut = guess; + installed = true; + + if (setenv("CHPL_HOME", guessFromPrefix.c_str(), 0)) { + return std::error_code(errno, std::system_category()); } + + chplHomeOut = guessFromPrefix; } + + if (chplHomeOut.empty()) { + diagnosticMessage = "CHPL_HOME must be set"; + return std::make_error_code(std::errc::no_such_file_or_directory); + } + // Check that the resulting path is a Chapel distribution. if (!isMaybeChplHome(chplHomeOut.c_str())) { - // Bad enviro var. - //USR_WARN("CHPL_HOME=%s is not a Chapel distribution", CHPL_HOME); - warningMessage = "CHPL_HOME=" + chplHomeOut + " is not a Chapel distribution"; + diagnosticMessage = "CHPL_HOME=" + chplHomeOut + " is not a Chapel distribution"; } return std::error_code(); } diff --git a/tools/c2chapel/c2chapel b/tools/c2chapel/c2chapel index e9eddbc6c165..b3b1b2bc3152 100755 --- a/tools/c2chapel/c2chapel +++ b/tools/c2chapel/c2chapel @@ -21,8 +21,14 @@ # if [ -z "$CHPL_HOME" ]; then - echo "Error: CHPL_HOME is not set" - exit 1 + # We need CHPL_HOME to find run-in-venv.bash. Try falling back to a + # compiler in PATH. + if output=$(chpl --print-bootstrap-commands); then + eval "$output" + else + echo "Error: CHPL_HOME is not set" 1>&2 + exit 1 + fi fi exec $CHPL_HOME/util/config/run-in-venv.bash \ diff --git a/tools/chpl-language-server/chpl-language-server b/tools/chpl-language-server/chpl-language-server index 2b053655877f..bf7e350a98d6 100755 --- a/tools/chpl-language-server/chpl-language-server +++ b/tools/chpl-language-server/chpl-language-server @@ -20,8 +20,14 @@ # if [ -z "$CHPL_HOME" ]; then - echo "Error: CHPL_HOME is not set" - exit 1 + # We need CHPL_HOME to find run-in-venv.bash. Try falling back to a + # compiler in PATH. + if output=$(chpl --print-bootstrap-commands); then + eval "$output" + else + echo "Error: CHPL_HOME is not set" 1>&2 + exit 1 + fi fi exec $CHPL_HOME/util/config/run-in-venv-with-python-bindings.bash \ diff --git a/tools/chplcheck/chplcheck b/tools/chplcheck/chplcheck index 4e752c130bce..7aef1f2b33d5 100755 --- a/tools/chplcheck/chplcheck +++ b/tools/chplcheck/chplcheck @@ -21,8 +21,14 @@ # if [ -z "$CHPL_HOME" ]; then - echo "Error: CHPL_HOME is not set" - exit 1 + # We need CHPL_HOME to find run-in-venv.bash. Try falling back to a + # compiler in PATH. + if output=$(chpl --print-bootstrap-commands); then + eval "$output" + else + echo "Error: CHPL_HOME is not set" 1>&2 + exit 1 + fi fi exec $CHPL_HOME/util/config/run-in-venv-with-python-bindings.bash \ diff --git a/tools/chpldoc/chpldoc.cpp b/tools/chpldoc/chpldoc.cpp index 314c171eeac1..a5621a61e668 100644 --- a/tools/chpldoc/chpldoc.cpp +++ b/tools/chpldoc/chpldoc.cpp @@ -2269,13 +2269,13 @@ class ChpldocErrorHandler : public Context::ErrorHandler { int main(int argc, char** argv) { Args args = parseArgs(argc, argv, (void*)main); - std::string warningMsg; + std::string diagnosticMsg; bool foundEnv = false; bool installed = false; // if user overrides CHPL_HOME from command line, don't go looking for trouble if (CHPL_HOME.empty()) { std::error_code err = findChplHome(argv[0], (void*)main, CHPL_HOME, - installed, foundEnv, warningMsg); + installed, foundEnv, diagnosticMsg); if (installed) { // need to determine and update third-party location before calling // getChplDepsApp to make sure we get an updated path in the case @@ -2285,12 +2285,16 @@ int main(int argc, char** argv) { CHPL_THIRD_PARTY += getMajorMinorVersion(); CHPL_THIRD_PARTY += "/third-party"; } - if (!warningMsg.empty()) { - fprintf(stderr, "%s\n", warningMsg.c_str()); - } - if (err) { + + // When error code is set, diagnosticMsg contains the error. + if (!diagnosticMsg.empty()) { + fprintf(stderr, "%s\n", diagnosticMsg.c_str()); + } else if (err) { fprintf(stderr, "CHPL_HOME not set to a valid value. Please set CHPL_HOME or pass a value " "using the --home option\n" ); + } + + if (err) { clean_exit(1); } } diff --git a/util/buildRelease/install.sh b/util/buildRelease/install.sh index ed19e309a2e6..544c69b3282f 100755 --- a/util/buildRelease/install.sh +++ b/util/buildRelease/install.sh @@ -338,6 +338,55 @@ then fi fi +C2CHAPEL="bin/$CHPL_BIN_SUBDIR"/c2chapel + +# copy c2chapel +if [ -f "$C2CHAPEL" ] +then + myinstalldir "tools/c2chapel/install" "$DEST_CHPL_HOME/tools/c2chapel/install" + myinstallfile "tools/c2chapel/c2chapel" "$DEST_CHPL_HOME/tools/c2chapel" + myinstallfile "tools/c2chapel/c2chapel.py" "$DEST_CHPL_HOME/tools/c2chapel" + myinstallfile "tools/c2chapel/utils/custom.h" "$DEST_CHPL_HOME/tools/c2chapel/util" + + if [ ! -z "$PREFIX" ] + then + ln -s "$DEST_CHPL_HOME/tools/c2chapel/c2chapel" "$PREFIX/bin"/c2chapel + else + ln -s "$DEST_CHPL_HOME/tools/c2chapel/c2chapel" "$DEST_DIR/bin/$CHPL_BIN_SUBDIR"/c2chapel + fi +fi + +CHPLCHECK="bin/$CHPL_BIN_SUBDIR"/chplcheck + +# copy chplcheck +if [ -f "$CHPLCHECK" ] +then + myinstallfile "tools/chplcheck/chplcheck" "$DEST_CHPL_HOME/tools/chplcheck" + myinstalldir "tools/chplcheck/src" "$DEST_CHPL_HOME/tools/chplcheck/src" + + if [ ! -z "$PREFIX" ] + then + ln -s "$DEST_CHPL_HOME/tools/chplcheck/chplcheck" "$PREFIX/bin"/chplcheck + else + ln -s "$DEST_CHPL_HOME/tools/chplcheck/chplcheck" "$DEST_DIR/bin/$CHPL_BIN_SUBDIR"/chplcheck + fi +fi + +CHPL_LANGUAGE_SERVER="bin/$CHPL_BIN_SUBDIR"/chpl-language-server + +# copy chpl-language-server +if [ -f "$CHPL_LANGUAGE_SERVER" ] +then + myinstallfile "tools/chpl-language-server/chpl-language-server" "$DEST_CHPL_HOME/tools/chpl-language-server" + myinstalldir "tools/chpl-language-server/src" "$DEST_CHPL_HOME/tools/chpl-language-server/src" + + if [ ! -z "$PREFIX" ] + then + ln -s "$DEST_CHPL_HOME/tools/chpl-language-server/chpl-language-server" "$PREFIX/bin"/chpl-language-server + else + ln -s "$DEST_CHPL_HOME/tools/chpl-language-server/chpl-language-server" "$DEST_DIR/bin/$CHPL_BIN_SUBDIR"/chpl-language-server + fi +fi # copy chplconfig if [ -f chplconfig ] diff --git a/util/chpl-completion.bash b/util/chpl-completion.bash index f370c5344d05..79e8f958ab57 100644 --- a/util/chpl-completion.bash +++ b/util/chpl-completion.bash @@ -317,6 +317,7 @@ _chpl () --preserve-inlined-line-numbers \ --print-additional-errors \ --print-all-candidates \ +--print-bootstrap-commands \ --print-callgraph \ --print-callstack-on-error \ --print-chpl-home \