diff --git a/flang/tools/f18/CMakeLists.txt b/flang/tools/f18/CMakeLists.txt
--- a/flang/tools/f18/CMakeLists.txt
+++ b/flang/tools/f18/CMakeLists.txt
@@ -50,10 +50,11 @@
# This flang shell script will only work in a POSIX shell.
if (NOT WIN32)
- add_custom_command(
- OUTPUT ${CMAKE_BINARY_DIR}/bin/flang-to-external-fc
- DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/flang-to-external-fc
- COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/flang-to-external-fc ${CMAKE_BINARY_DIR}/bin)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/flang-to-external-fc.in
+ ${CMAKE_BINARY_DIR}/bin/flang-to-external-fc
+ @ONLY
+ )
add_custom_target(flang-to-external-fc ALL DEPENDS ${CMAKE_BINARY_DIR}/bin/flang-to-external-fc)
install(PROGRAMS ${CMAKE_BINARY_DIR}/bin/flang-to-external-fc DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif()
diff --git a/flang/tools/f18/flang-to-external-fc b/flang/tools/f18/flang-to-external-fc
deleted file mode 100755
--- a/flang/tools/f18/flang-to-external-fc
+++ /dev/null
@@ -1,493 +0,0 @@
-#! /usr/bin/env bash
-#===-- tools/f18/flang-to-external-fc.sh --------------------------*- sh -*-===#
-#
-# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#
-#===------------------------------------------------------------------------===#
-# A wrapper script for Flang's compiler driver that was developed for testing and
-# experimenting. You should be able to use it as a regular compiler driver. It
-# will:
-# * run Flang's compiler driver to unparse the input source files
-# * use the external compiler (defined via FLANG_FC environment variable) to
-# compile the unparsed source files
-#
-# Tested with Bash 4.4. This script will exit immediately if you use an
-# older version of Bash.
-#===------------------------------------------------------------------------===#
-set -euo pipefail
-
-# Global variables to make the parsing of input arguments a bit easier
-INPUT_FILES=()
-OPTIONS=()
-OUTPUT_FILE=""
-MODULE_DIR=""
-INTRINSICS_MOD_DIR=""
-COMPILE_ONLY="False"
-PREPROCESS_ONLY="False"
-PRINT_VERSION="False"
-
-# === check_bash_version ======================================================
-#
-# Checks the Bash version that's used to run this script. Exits immediately
-# with a non-zero return code if it's lower than 4.4. Otherwise returns 0
-# (success).
-# =============================================================================
-check_bash_version() {
- message="Error: Your Bash is too old. Please use Bash >= 4.4"
- # Major version
- [[ "${BASH_VERSINFO[0]:-0}" -lt 4 ]] && echo $message && exit 1
-
- # Minor version
- if [[ "${BASH_VERSINFO[0]}" == 4 ]]; then
- [[ "${BASH_VERSINFO[1]:-0}" -lt 4 ]] && echo $message && exit 1
- fi
-
- return 0
-}
-
-# === parse_args ==============================================================
-#
-# Parse the input arguments passed to this script. Sets the global variables
-# declared at the top.
-#
-# INPUTS:
-# $1 - all input arguments
-# OUTPUTS:
-# Saved in the global variables for this script
-# =============================================================================
-parse_args()
-{
- while [ "${1:-}" != "" ]; do
- # CASE 1: Compiler option
- if [[ "${1:0:1}" == "-" ]] ; then
- # Output file - extract it into a global variable
- if [[ "$1" == "-o" ]] ; then
- shift
- OUTPUT_FILE="$1"
- shift
- continue
- fi
-
- # Module directory - extract it into a global variable
- if [[ "$1" == "-module-dir" ]]; then
- shift
- MODULE_DIR="$1"
- shift
- continue
- fi
-
- # Intrinsics module dir - extract it into a global var
- if [[ "$1" == "-intrinsics-module-directory" ]]; then shift
- INTRINSICS_MOD_DIR=$1
- shift
- continue
- fi
-
- # Module suffix cannot be modified - this script defines it before
- # calling the driver.
- if [[ "$1" == "-module-suffix" ]]; then
- echo "ERROR: \'-module-suffix\' is not available when using the \'flang\' script"
- exit 1
- fi
-
- # Special treatment for `J
` and `-I `. We translate these
- # into `J` and `-I` respectively.
- if [[ "$1" == "-J" ]] || [[ "$1" == "-I" ]]; then
- opt=$1
- shift
- OPTIONS+=("$opt$1")
- shift
- continue
- fi
-
- # This is a regular option - just add it to the list.
- OPTIONS+=($1)
- if [[ $1 == "-c" ]]; then
- COMPILE_ONLY="True"
- fi
-
- if [[ $1 == "-E" ]]; then
- PREPROCESS_ONLY="True"
- fi
-
- if [[ $1 == "-v" || $1 == "--version" ]]; then
- PRINT_VERSION="True"
- fi
-
- shift
- continue
-
- # CASE 2: A regular file (either source or a library file)
- elif [[ -f "$1" ]]; then
- INPUT_FILES+=($1)
- shift
- continue
-
- else
- # CASE 3: Unsupported
- echo "ERROR: unrecognised option format: \`$1\`. Perhaps non-existent file?"
- exit 1
- fi
- done
-}
-
-# === categorise_files ========================================================
-#
-# Categorises input files into:
-# * Fortran source files (to be compiled)
-# * library files (to be linked into the final executable)
-#
-# INPUTS:
-# $1 - all input files to be categorised (array, name reference)
-# OUTPUTS:
-# $2 - Fortran source files extracted from $1 (array, name reference)
-# $3 - other source files extracted from $1 (array, name reference)
-# $4 - object files extracted from $1 (array, name reference)
-# $5 - lib files extracted from $1 (array, name reference)
-# =============================================================================
-categorise_files()
-{
- local -n -r all_files=$1
- local -n fortran_sources=$2
- local -n other_sources=$3
- local -n objects=$4
- local -n libs=$5
-
- for current_file in "${all_files[@]}"; do
- file_ext=${current_file##*.}
- if [[ $file_ext == "f" ]] || [[ $file_ext == "f90" ]] ||
- [[ $file_ext == "f" ]] || [[ $file_ext == "F" ]] || [[ $file_ext == "ff" ]] ||
- [[ $file_ext == "f90" ]] || [[ $file_ext == "F90" ]] || [[ $file_ext == "ff90" ]] ||
- [[ $file_ext == "f95" ]] || [[ $file_ext == "F95" ]] || [[ $file_ext == "ff95" ]] ||
- [[ $file_ext == "cuf" ]] || [[ $file_ext == "CUF" ]] || [[ $file_ext == "f18" ]] ||
- [[ $file_ext == "F18" ]] || [[ $file_ext == "ff18" ]]; then
- fortran_sources+=($current_file)
- elif [[ $file_ext == "a" ]] || [[ $file_ext == "so" ]]; then
- libs+=($current_file)
- elif [[ $file_ext == "o" ]]; then
- objects+=($current_file)
- else
- other_sources+=($current_file)
- fi
- done
-}
-
-# === categorise_opts ==========================================================
-#
-# Categorises compiler options into options for:
-# * the Flang driver (either new or the "throwaway" driver)
-# * the external Fortran driver that will generate the code
-# Most options accepted by Flang will be claimed by it. The only exceptions are
-# `-I` and `-J`.
-#
-# INPUTS:
-# $1 - all compiler options (array, name reference)
-# OUTPUTS:
-# $2 - compiler options for the Flang driver (array, name reference)
-# $3 - compiler options for the external driver (array, name reference)
-# =============================================================================
-categorise_opts()
-{
- local -n all_opts=$1
- local -n flang_opts=$2
- local -n fc_opts=$3
-
- for opt in "${all_opts[@]}"; do
- # These options are claimed by Flang, but should've been dealt with in parse_args.
- if [[ $opt == "-module-dir" ]] ||
- [[ $opt == "-o" ]] ||
- [[ $opt == "-fintrinsic-modules-path" ]] ; then
- echo "ERROR: $opt should've been fully processed by \`parse_args\`"
- exit 1
- fi
-
- if
- # The options claimed by Flang. This list needs to be compatible with
- # what's supported by Flang's compiler driver (i.e. `flang-new`).
- [[ $opt == "-cpp" ]] ||
- [[ $opt =~ ^-D.* ]] ||
- [[ $opt == "-E" ]] ||
- [[ $opt == "-falternative-parameter-statement" ]] ||
- [[ $opt == "-fbackslash" ]] ||
- [[ $opt == "-fcolor-diagnostics" ]] ||
- [[ $opt == "-fdefault-double-8" ]] ||
- [[ $opt == "-fdefault-integer-8" ]] ||
- [[ $opt == "-fdefault-real-8" ]] ||
- [[ $opt == "-ffixed-form" ]] ||
- [[ $opt =~ ^-ffixed-line-length=.* ]] ||
- [[ $opt == "-ffree-form" ]] ||
- [[ $opt == "-fimplicit-none" ]] ||
- [[ $opt =~ ^-finput-charset=.* ]] ||
- [[ $opt == "-flarge-sizes" ]] ||
- [[ $opt == "-flogical-abbreviations" ]] ||
- [[ $opt == "-fno-color-diagnostics" ]] ||
- [[ $opt == "-fxor-operator" ]] ||
- [[ $opt == "-help" ]] ||
- [[ $opt == "-nocpp" ]] ||
- [[ $opt == "-pedantic" ]] ||
- [[ $opt =~ ^-std=.* ]] ||
- [[ $opt =~ ^-U.* ]] ||
- [[ $opt == "-Werror" ]]; then
- flang_opts+=($opt)
- elif
- # We translate the following into equivalents understood by `flang-new`
- [[ $opt == "-Mfixed" ]] || [[ $opt == "-Mfree" ]]; then
- case $opt in
- -Mfixed)
- flang_opts+=("-ffixed-form")
- ;;
-
- -Mfree)
- flang_opts+=("-ffree-form")
- ;;
-
- *)
- echo "ERROR: $opt has no equivalent in 'flang-new'"
- exit 1
- ;;
- esac
- elif
- # Options that are needed for both Flang and the external driver.
- [[ $opt =~ -I.* ]] ||
- [[ $opt =~ -J.* ]] ||
- [[ $opt == "-fopenmp" ]] ||
- [[ $opt == "-fopenacc" ]]; then
- flang_opts+=($opt)
- fc_opts+=($opt)
- else
- # All other options are claimed for the external driver.
- fc_opts+=($opt)
- fi
- done
-}
-
-# === get_external_fc_name ====================================================
-#
-# Returns the name of external Fortran compiler based on values of
-# environment variables.
-# =============================================================================
-get_external_fc_name() {
- if [[ -v FLANG_FC ]]; then
- echo ${FLANG_FC}
- elif [[ -v F18_FC ]]; then
- # We support F18_FC for backwards compatibility.
- echo ${F18_FC}
- else
- echo gfortran
- fi
-}
-
-# === preprocess ==============================================================
-#
-# Runs the preprocessing. Fortran files are preprocessed using Flang. Other
-# files are preprocessed using the external Fortran compiler.
-#
-# INPUTS:
-# $1 - Fortran source files (array, name reference)
-# $2 - other source files (array, name reference)
-# $3 - compiler flags (array, name reference)
-# =============================================================================
-preprocess() {
- local -n fortran_srcs=$1
- local -n other_srcs=$2
- local -n opts=$3
-
- local ext_fc="$(get_external_fc_name)"
-
- local -r wd=$(cd "$(dirname "$0")/.." && pwd)
-
- # Use the provided output file name.
- if [[ ! -z ${OUTPUT_FILE:+x} ]]; then
- output_definition="-o $OUTPUT_FILE"
- fi
-
- # Preprocess fortran sources using Flang
- for idx in "${!fortran_srcs[@]}"; do
- if ! "$wd/bin/flang-new" -E "${opts[@]}" "${fortran_srcs[$idx]}" ${output_definition:+$output_definition}
- then status=$?
- echo flang: in "$PWD", flang-new failed with exit status $status: "$wd/bin/flang-new" "${opts[@]}" "$@" >&2
- exit $status
- fi
- done
-
- # Preprocess other sources using Flang
- for idx in "${!other_srcs[@]}"; do
- if ! $ext_fc -E "${opts[@]}" "${other_srcs[$idx]}" ${output_definition:+$output_definition}
- then status=$?
- echo flang: in "$PWD", flang-new failed with exit status $status: "$wd/bin/flang-new" "${opts[@]}" "$@" >&2
- exit $status
- fi
- done
-}
-
-# === get_relocatable_name ======================================================
-# This method generates the name of the output file for the compilation phase
-# (triggered with `-c`). If the user of this script is only interested in
-# compilation (`flang -c`), use $OUTPUT_FILE provided that it was defined.
-# Otherwise, use the usual heuristics:
-# * file.f --> file.o
-# * file.c --> file.o
-#
-# INPUTS:
-# $1 - input source file for which to generate the output name
-# =============================================================================
-get_relocatable_name() {
- local -r src_file=$1
-
- if [[ $COMPILE_ONLY == "True" ]] && [[ ! -z ${OUTPUT_FILE:+x} ]]; then
- out_file="$OUTPUT_FILE"
- else
- current_ext=${src_file##*.}
- new_ext="o"
-
- out_file=$(basename "${src_file}" "$current_ext")${new_ext}
- fi
-
- echo "$out_file"
-}
-
-# === main ====================================================================
-# Main entry point for this script
-# =============================================================================
-main() {
- check_bash_version
- parse_args "$@"
-
- if [[ $PRINT_VERSION == "True" ]]; then
- echo "flang version @FLANG_VERSION@"
- exit 0
- fi
-
- # Source, object and library files provided by the user
- local fortran_source_files=()
- local other_source_files=()
- local object_files=()
- local lib_files=()
- categorise_files INPUT_FILES fortran_source_files other_source_files object_files lib_files
-
- if [[ $PREPROCESS_ONLY == "True" ]]; then
- preprocess fortran_source_files other_source_files OPTIONS
- exit 0
- fi
-
- # Options for the Flang driver.
- # NOTE: We need `-fc1` to make sure that the frontend driver rather than
- # compiler driver is used. We also need to make sure that that's the first
- # flag that the driver will see (otherwise it assumes compiler/toolchain
- # driver mode).
- local flang_options=("-fc1")
- # Options for the external Fortran Compiler
- local ext_fc_options=()
- categorise_opts OPTIONS flang_options ext_fc_options
-
- local -r wd=$(cd "$(dirname "$0")/.." && pwd)
-
- # uuidgen is common but not installed by default on some distros
- if ! command -v uuidgen &> /dev/null
- then
- echo "uuidgen is required for generating unparsed file names."
- exit 1
- fi
-
- # STEP 1: Unparse
- # Base-name for the unparsed files. These are just temporary files that are
- # first generated and then deleted by this script.
- # NOTE: We need to make sure that the base-name is unique to every
- # invocation. Otherwise we can't use this script in parallel.
- local -r unique_id=$(uuidgen | cut -b25-36)
- local -r unparsed_file_base="flang_unparsed_file_$unique_id"
-
- flang_options+=("-module-suffix")
- flang_options+=(".f18.mod")
- flang_options+=("-fdebug-unparse")
- flang_options+=("-fno-analyzed-objects-for-unparse")
-
- [[ ! -z ${MODULE_DIR} ]] && flang_options+=("-module-dir ${MODULE_DIR}")
- [[ ! -z ${INTRINSICS_MOD_DIR} ]] && flang_options+=("-intrinsics-module-directory ${INTRINSICS_MOD_DIR}")
- for idx in "${!fortran_source_files[@]}"; do
- set +e
- "$wd/bin/flang-new" "${flang_options[@]}" "${fortran_source_files[$idx]}" -o "${unparsed_file_base}_${idx}.f90"
- ret_status=$?
- set -e
- if [[ $ret_status != 0 ]]; then
- echo flang: in "$PWD", flang-new failed with exit status "$ret_status": "$wd/bin/flang-new" "${flang_options[@]}" "$@" >&2
- exit "$ret_status"
- fi
- done
-
- # STEP 2: Compile Fortran Source Files
- local ext_fc="$(get_external_fc_name)"
- # Temporary object files generated by this script. To be deleted at the end.
- local temp_object_files=()
- for idx in "${!fortran_source_files[@]}"; do
- # We always have to specify the output name with `-o `. This
- # is because we are using the unparsed rather than the original source file
- # below. As a result, we cannot rely on the compiler-generated output name.
- out_obj_file=$(get_relocatable_name "${fortran_source_files[$idx]}")
-
- set +e
- $ext_fc "-c" "${ext_fc_options[@]}" "${unparsed_file_base}_${idx}.f90" "-o" "${out_obj_file}"
- ret_status=$?
- set -e
- if [[ $ret_status != 0 ]]; then
- echo flang: in "$PWD", "$ext_fc" failed with exit status "$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
- exit "$ret_status"
- fi
- temp_object_files+=(${out_obj_file})
- done
-
- # Delete the unparsed files
- for idx in "${!fortran_source_files[@]}"; do
- rm "${unparsed_file_base}_${idx}.f90"
- done
-
- # STEP 3: Compile Other Source Files
- for idx in "${!other_source_files[@]}"; do
- # We always specify the output name with `-o `. The user
- # might have used `-o`, but we never add it to $OPTIONS (or
- # $ext_fc_options). Hence we need to use `get_relocatable_name`.
- out_obj_file=$(get_relocatable_name "${other_source_files[$idx]}")
-
- set +e
- $ext_fc "-c" "${ext_fc_options[@]}" "${other_source_files[${idx}]}" "-o" "${out_obj_file}"
- ret_status=$?
- set -e
- if [[ $ret_status != 0 ]]; then
- echo flang: in "$PWD", "$ext_fc" failed with exit status "$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
- exit "$ret_status"
- fi
- temp_object_files+=(${out_obj_file})
- done
-
- # STEP 4: Link
- if [[ $COMPILE_ONLY == "True" ]]; then
- exit 0;
- fi
-
- if [[ ${#temp_object_files[@]} -ge 1 ]] || [[ ${#object_files[@]} -ge 1 ]] ; then
- # If $OUTPUT_FILE was specified, use it for the output name.
- if [[ ! -z ${OUTPUT_FILE:+x} ]]; then
- output_definition="-o $OUTPUT_FILE"
- else
- output_definition=""
- fi
-
- set +e
- $ext_fc "${ext_fc_options[@]}" "${object_files[@]}" "${temp_object_files[@]}" "${lib_files[@]}" ${output_definition:+$output_definition}
- ret_status=$?
- set -e
- if [[ $ret_status != 0 ]]; then
- echo flang: in "$PWD", "$ext_fc" failed with exit status "$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
- exit "$ret_status"
- fi
- fi
-
- # Delete intermediate object files
- for idx in "${!fortran_source_files[@]}"; do
- rm "${temp_object_files[$idx]}"
- done
-}
-
-main "${@}"
diff --git a/flang/tools/f18/flang-to-external-fc.in b/flang/tools/f18/flang-to-external-fc.in
new file mode 100755
--- /dev/null
+++ b/flang/tools/f18/flang-to-external-fc.in
@@ -0,0 +1,493 @@
+#! /usr/bin/env bash
+#===-- tools/f18/flang-to-external-fc.sh --------------------------*- sh -*-===#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===------------------------------------------------------------------------===#
+# A wrapper script for Flang's compiler driver that was developed for testing and
+# experimenting. You should be able to use it as a regular compiler driver. It
+# will:
+# * run Flang's compiler driver to unparse the input source files
+# * use the external compiler (defined via FLANG_FC environment variable) to
+# compile the unparsed source files
+#
+# Tested with Bash 4.4. This script will exit immediately if you use an
+# older version of Bash.
+#===------------------------------------------------------------------------===#
+set -euo pipefail
+
+# Global variables to make the parsing of input arguments a bit easier
+INPUT_FILES=()
+OPTIONS=()
+OUTPUT_FILE=""
+MODULE_DIR=""
+INTRINSICS_MOD_DIR=""
+COMPILE_ONLY="False"
+PREPROCESS_ONLY="False"
+PRINT_VERSION="False"
+
+# === check_bash_version ======================================================
+#
+# Checks the Bash version that's used to run this script. Exits immediately
+# with a non-zero return code if it's lower than 4.4. Otherwise returns 0
+# (success).
+# =============================================================================
+check_bash_version() {
+ message="Error: Your Bash is too old. Please use Bash >= 4.4"
+ # Major version
+ [[ "${BASH_VERSINFO[0]:-0}" -lt 4 ]] && echo $message && exit 1
+
+ # Minor version
+ if [[ "${BASH_VERSINFO[0]}" == 4 ]]; then
+ [[ "${BASH_VERSINFO[1]:-0}" -lt 4 ]] && echo $message && exit 1
+ fi
+
+ return 0
+}
+
+# === parse_args ==============================================================
+#
+# Parse the input arguments passed to this script. Sets the global variables
+# declared at the top.
+#
+# INPUTS:
+# $1 - all input arguments
+# OUTPUTS:
+# Saved in the global variables for this script
+# =============================================================================
+parse_args()
+{
+ while [ "${1:-}" != "" ]; do
+ # CASE 1: Compiler option
+ if [[ "${1:0:1}" == "-" ]] ; then
+ # Output file - extract it into a global variable
+ if [[ "$1" == "-o" ]] ; then
+ shift
+ OUTPUT_FILE="$1"
+ shift
+ continue
+ fi
+
+ # Module directory - extract it into a global variable
+ if [[ "$1" == "-module-dir" ]]; then
+ shift
+ MODULE_DIR="$1"
+ shift
+ continue
+ fi
+
+ # Intrinsics module dir - extract it into a global var
+ if [[ "$1" == "-intrinsics-module-directory" ]]; then shift
+ INTRINSICS_MOD_DIR=$1
+ shift
+ continue
+ fi
+
+ # Module suffix cannot be modified - this script defines it before
+ # calling the driver.
+ if [[ "$1" == "-module-suffix" ]]; then
+ echo "ERROR: \'-module-suffix\' is not available when using the \'flang\' script"
+ exit 1
+ fi
+
+ # Special treatment for `J ` and `-I `. We translate these
+ # into `J` and `-I` respectively.
+ if [[ "$1" == "-J" ]] || [[ "$1" == "-I" ]]; then
+ opt=$1
+ shift
+ OPTIONS+=("$opt$1")
+ shift
+ continue
+ fi
+
+ # This is a regular option - just add it to the list.
+ OPTIONS+=($1)
+ if [[ $1 == "-c" ]]; then
+ COMPILE_ONLY="True"
+ fi
+
+ if [[ $1 == "-E" ]]; then
+ PREPROCESS_ONLY="True"
+ fi
+
+ if [[ $1 == "-v" || $1 == "--version" ]]; then
+ PRINT_VERSION="True"
+ fi
+
+ shift
+ continue
+
+ # CASE 2: A regular file (either source or a library file)
+ elif [[ -f "$1" ]]; then
+ INPUT_FILES+=($1)
+ shift
+ continue
+
+ else
+ # CASE 3: Unsupported
+ echo "ERROR: unrecognised option format: \`$1\`. Perhaps non-existent file?"
+ exit 1
+ fi
+ done
+}
+
+# === categorise_files ========================================================
+#
+# Categorises input files into:
+# * Fortran source files (to be compiled)
+# * library files (to be linked into the final executable)
+#
+# INPUTS:
+# $1 - all input files to be categorised (array, name reference)
+# OUTPUTS:
+# $2 - Fortran source files extracted from $1 (array, name reference)
+# $3 - other source files extracted from $1 (array, name reference)
+# $4 - object files extracted from $1 (array, name reference)
+# $5 - lib files extracted from $1 (array, name reference)
+# =============================================================================
+categorise_files()
+{
+ local -n -r all_files=$1
+ local -n fortran_sources=$2
+ local -n other_sources=$3
+ local -n objects=$4
+ local -n libs=$5
+
+ for current_file in "${all_files[@]}"; do
+ file_ext=${current_file##*.}
+ if [[ $file_ext == "f" ]] || [[ $file_ext == "f90" ]] ||
+ [[ $file_ext == "f" ]] || [[ $file_ext == "F" ]] || [[ $file_ext == "ff" ]] ||
+ [[ $file_ext == "f90" ]] || [[ $file_ext == "F90" ]] || [[ $file_ext == "ff90" ]] ||
+ [[ $file_ext == "f95" ]] || [[ $file_ext == "F95" ]] || [[ $file_ext == "ff95" ]] ||
+ [[ $file_ext == "cuf" ]] || [[ $file_ext == "CUF" ]] || [[ $file_ext == "f18" ]] ||
+ [[ $file_ext == "F18" ]] || [[ $file_ext == "ff18" ]]; then
+ fortran_sources+=($current_file)
+ elif [[ $file_ext == "a" ]] || [[ $file_ext == "so" ]]; then
+ libs+=($current_file)
+ elif [[ $file_ext == "o" ]]; then
+ objects+=($current_file)
+ else
+ other_sources+=($current_file)
+ fi
+ done
+}
+
+# === categorise_opts ==========================================================
+#
+# Categorises compiler options into options for:
+# * the Flang driver (either new or the "throwaway" driver)
+# * the external Fortran driver that will generate the code
+# Most options accepted by Flang will be claimed by it. The only exceptions are
+# `-I` and `-J`.
+#
+# INPUTS:
+# $1 - all compiler options (array, name reference)
+# OUTPUTS:
+# $2 - compiler options for the Flang driver (array, name reference)
+# $3 - compiler options for the external driver (array, name reference)
+# =============================================================================
+categorise_opts()
+{
+ local -n all_opts=$1
+ local -n flang_opts=$2
+ local -n fc_opts=$3
+
+ for opt in "${all_opts[@]}"; do
+ # These options are claimed by Flang, but should've been dealt with in parse_args.
+ if [[ $opt == "-module-dir" ]] ||
+ [[ $opt == "-o" ]] ||
+ [[ $opt == "-fintrinsic-modules-path" ]] ; then
+ echo "ERROR: $opt should've been fully processed by \`parse_args\`"
+ exit 1
+ fi
+
+ if
+ # The options claimed by Flang. This list needs to be compatible with
+ # what's supported by Flang's compiler driver (i.e. `flang-new`).
+ [[ $opt == "-cpp" ]] ||
+ [[ $opt =~ ^-D.* ]] ||
+ [[ $opt == "-E" ]] ||
+ [[ $opt == "-falternative-parameter-statement" ]] ||
+ [[ $opt == "-fbackslash" ]] ||
+ [[ $opt == "-fcolor-diagnostics" ]] ||
+ [[ $opt == "-fdefault-double-8" ]] ||
+ [[ $opt == "-fdefault-integer-8" ]] ||
+ [[ $opt == "-fdefault-real-8" ]] ||
+ [[ $opt == "-ffixed-form" ]] ||
+ [[ $opt =~ ^-ffixed-line-length=.* ]] ||
+ [[ $opt == "-ffree-form" ]] ||
+ [[ $opt == "-fimplicit-none" ]] ||
+ [[ $opt =~ ^-finput-charset=.* ]] ||
+ [[ $opt == "-flarge-sizes" ]] ||
+ [[ $opt == "-flogical-abbreviations" ]] ||
+ [[ $opt == "-fno-color-diagnostics" ]] ||
+ [[ $opt == "-fxor-operator" ]] ||
+ [[ $opt == "-help" ]] ||
+ [[ $opt == "-nocpp" ]] ||
+ [[ $opt == "-pedantic" ]] ||
+ [[ $opt =~ ^-std=.* ]] ||
+ [[ $opt =~ ^-U.* ]] ||
+ [[ $opt == "-Werror" ]]; then
+ flang_opts+=($opt)
+ elif
+ # We translate the following into equivalents understood by `flang-new`
+ [[ $opt == "-Mfixed" ]] || [[ $opt == "-Mfree" ]]; then
+ case $opt in
+ -Mfixed)
+ flang_opts+=("-ffixed-form")
+ ;;
+
+ -Mfree)
+ flang_opts+=("-ffree-form")
+ ;;
+
+ *)
+ echo "ERROR: $opt has no equivalent in 'flang-new'"
+ exit 1
+ ;;
+ esac
+ elif
+ # Options that are needed for both Flang and the external driver.
+ [[ $opt =~ -I.* ]] ||
+ [[ $opt =~ -J.* ]] ||
+ [[ $opt == "-fopenmp" ]] ||
+ [[ $opt == "-fopenacc" ]]; then
+ flang_opts+=($opt)
+ fc_opts+=($opt)
+ else
+ # All other options are claimed for the external driver.
+ fc_opts+=($opt)
+ fi
+ done
+}
+
+# === get_external_fc_name ====================================================
+#
+# Returns the name of external Fortran compiler based on values of
+# environment variables.
+# =============================================================================
+get_external_fc_name() {
+ if [[ -v FLANG_FC ]]; then
+ echo ${FLANG_FC}
+ elif [[ -v F18_FC ]]; then
+ # We support F18_FC for backwards compatibility.
+ echo ${F18_FC}
+ else
+ echo gfortran
+ fi
+}
+
+# === preprocess ==============================================================
+#
+# Runs the preprocessing. Fortran files are preprocessed using Flang. Other
+# files are preprocessed using the external Fortran compiler.
+#
+# INPUTS:
+# $1 - Fortran source files (array, name reference)
+# $2 - other source files (array, name reference)
+# $3 - compiler flags (array, name reference)
+# =============================================================================
+preprocess() {
+ local -n fortran_srcs=$1
+ local -n other_srcs=$2
+ local -n opts=$3
+
+ local ext_fc="$(get_external_fc_name)"
+
+ local -r wd=$(cd "$(dirname "$0")/.." && pwd)
+
+ # Use the provided output file name.
+ if [[ ! -z ${OUTPUT_FILE:+x} ]]; then
+ output_definition="-o $OUTPUT_FILE"
+ fi
+
+ # Preprocess fortran sources using Flang
+ for idx in "${!fortran_srcs[@]}"; do
+ if ! "$wd/bin/flang-new" -E "${opts[@]}" "${fortran_srcs[$idx]}" ${output_definition:+$output_definition}
+ then status=$?
+ echo flang: in "$PWD", flang-new failed with exit status $status: "$wd/bin/flang-new" "${opts[@]}" "$@" >&2
+ exit $status
+ fi
+ done
+
+ # Preprocess other sources using Flang
+ for idx in "${!other_srcs[@]}"; do
+ if ! $ext_fc -E "${opts[@]}" "${other_srcs[$idx]}" ${output_definition:+$output_definition}
+ then status=$?
+ echo flang: in "$PWD", flang-new failed with exit status $status: "$wd/bin/flang-new" "${opts[@]}" "$@" >&2
+ exit $status
+ fi
+ done
+}
+
+# === get_relocatable_name ======================================================
+# This method generates the name of the output file for the compilation phase
+# (triggered with `-c`). If the user of this script is only interested in
+# compilation (`flang -c`), use $OUTPUT_FILE provided that it was defined.
+# Otherwise, use the usual heuristics:
+# * file.f --> file.o
+# * file.c --> file.o
+#
+# INPUTS:
+# $1 - input source file for which to generate the output name
+# =============================================================================
+get_relocatable_name() {
+ local -r src_file=$1
+
+ if [[ $COMPILE_ONLY == "True" ]] && [[ ! -z ${OUTPUT_FILE:+x} ]]; then
+ out_file="$OUTPUT_FILE"
+ else
+ current_ext=${src_file##*.}
+ new_ext="o"
+
+ out_file=$(basename "${src_file}" "$current_ext")${new_ext}
+ fi
+
+ echo "$out_file"
+}
+
+# === main ====================================================================
+# Main entry point for this script
+# =============================================================================
+main() {
+ check_bash_version
+ parse_args "$@"
+
+ if [[ $PRINT_VERSION == "True" ]]; then
+ echo "flang version @FLANG_VERSION@"
+ exit 0
+ fi
+
+ # Source, object and library files provided by the user
+ local fortran_source_files=()
+ local other_source_files=()
+ local object_files=()
+ local lib_files=()
+ categorise_files INPUT_FILES fortran_source_files other_source_files object_files lib_files
+
+ if [[ $PREPROCESS_ONLY == "True" ]]; then
+ preprocess fortran_source_files other_source_files OPTIONS
+ exit 0
+ fi
+
+ # Options for the Flang driver.
+ # NOTE: We need `-fc1` to make sure that the frontend driver rather than
+ # compiler driver is used. We also need to make sure that that's the first
+ # flag that the driver will see (otherwise it assumes compiler/toolchain
+ # driver mode).
+ local flang_options=("-fc1")
+ # Options for the external Fortran Compiler
+ local ext_fc_options=()
+ categorise_opts OPTIONS flang_options ext_fc_options
+
+ local -r wd=$(cd "$(dirname "$0")/.." && pwd)
+
+ # uuidgen is common but not installed by default on some distros
+ if ! command -v uuidgen &> /dev/null
+ then
+ echo "uuidgen is required for generating unparsed file names."
+ exit 1
+ fi
+
+ # STEP 1: Unparse
+ # Base-name for the unparsed files. These are just temporary files that are
+ # first generated and then deleted by this script.
+ # NOTE: We need to make sure that the base-name is unique to every
+ # invocation. Otherwise we can't use this script in parallel.
+ local -r unique_id=$(uuidgen | cut -b25-36)
+ local -r unparsed_file_base="flang_unparsed_file_$unique_id"
+
+ flang_options+=("-module-suffix")
+ flang_options+=(".f18.mod")
+ flang_options+=("-fdebug-unparse")
+ flang_options+=("-fno-analyzed-objects-for-unparse")
+
+ [[ ! -z ${MODULE_DIR} ]] && flang_options+=("-module-dir ${MODULE_DIR}")
+ [[ ! -z ${INTRINSICS_MOD_DIR} ]] && flang_options+=("-intrinsics-module-directory ${INTRINSICS_MOD_DIR}")
+ for idx in "${!fortran_source_files[@]}"; do
+ set +e
+ "$wd/bin/flang-new" "${flang_options[@]}" "${fortran_source_files[$idx]}" -o "${unparsed_file_base}_${idx}.f90"
+ ret_status=$?
+ set -e
+ if [[ $ret_status != 0 ]]; then
+ echo flang: in "$PWD", flang-new failed with exit status "$ret_status": "$wd/bin/flang-new" "${flang_options[@]}" "$@" >&2
+ exit "$ret_status"
+ fi
+ done
+
+ # STEP 2: Compile Fortran Source Files
+ local ext_fc="$(get_external_fc_name)"
+ # Temporary object files generated by this script. To be deleted at the end.
+ local temp_object_files=()
+ for idx in "${!fortran_source_files[@]}"; do
+ # We always have to specify the output name with `-o `. This
+ # is because we are using the unparsed rather than the original source file
+ # below. As a result, we cannot rely on the compiler-generated output name.
+ out_obj_file=$(get_relocatable_name "${fortran_source_files[$idx]}")
+
+ set +e
+ $ext_fc "-c" "${ext_fc_options[@]}" "${unparsed_file_base}_${idx}.f90" "-o" "${out_obj_file}"
+ ret_status=$?
+ set -e
+ if [[ $ret_status != 0 ]]; then
+ echo flang: in "$PWD", "$ext_fc" failed with exit status "$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
+ exit "$ret_status"
+ fi
+ temp_object_files+=(${out_obj_file})
+ done
+
+ # Delete the unparsed files
+ for idx in "${!fortran_source_files[@]}"; do
+ rm "${unparsed_file_base}_${idx}.f90"
+ done
+
+ # STEP 3: Compile Other Source Files
+ for idx in "${!other_source_files[@]}"; do
+ # We always specify the output name with `-o `. The user
+ # might have used `-o`, but we never add it to $OPTIONS (or
+ # $ext_fc_options). Hence we need to use `get_relocatable_name`.
+ out_obj_file=$(get_relocatable_name "${other_source_files[$idx]}")
+
+ set +e
+ $ext_fc "-c" "${ext_fc_options[@]}" "${other_source_files[${idx}]}" "-o" "${out_obj_file}"
+ ret_status=$?
+ set -e
+ if [[ $ret_status != 0 ]]; then
+ echo flang: in "$PWD", "$ext_fc" failed with exit status "$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
+ exit "$ret_status"
+ fi
+ temp_object_files+=(${out_obj_file})
+ done
+
+ # STEP 4: Link
+ if [[ $COMPILE_ONLY == "True" ]]; then
+ exit 0;
+ fi
+
+ if [[ ${#temp_object_files[@]} -ge 1 ]] || [[ ${#object_files[@]} -ge 1 ]] ; then
+ # If $OUTPUT_FILE was specified, use it for the output name.
+ if [[ ! -z ${OUTPUT_FILE:+x} ]]; then
+ output_definition="-o $OUTPUT_FILE"
+ else
+ output_definition=""
+ fi
+
+ set +e
+ $ext_fc "${ext_fc_options[@]}" "${object_files[@]}" "${temp_object_files[@]}" "${lib_files[@]}" ${output_definition:+$output_definition}
+ ret_status=$?
+ set -e
+ if [[ $ret_status != 0 ]]; then
+ echo flang: in "$PWD", "$ext_fc" failed with exit status "$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
+ exit "$ret_status"
+ fi
+ fi
+
+ # Delete intermediate object files
+ for idx in "${!fortran_source_files[@]}"; do
+ rm "${temp_object_files[$idx]}"
+ done
+}
+
+main "${@}"