2 #===-- tools/f18/flang-to-external-fc.sh --------------------------*- sh -*-===#
4 # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 # See https://llvm.org/LICENSE.txt for license information.
6 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 #===------------------------------------------------------------------------===#
9 # A wrapper script for Flang's compiler driver that was developed for testing and
10 # experimenting. You should be able to use it as a regular compiler driver. It
12 # * run Flang's compiler driver to unparse the input source files
13 # * use the external compiler (defined via FLANG_FC environment variable) to
14 # compile the unparsed source files
16 # Tested with Bash 4.4. This script will exit immediately if you use an
17 # older version of Bash.
18 #===------------------------------------------------------------------------===#
21 # Global variables to make the parsing of input arguments a bit easier
28 PREPROCESS_ONLY
="False"
31 # === check_bash_version ======================================================
33 # Checks the Bash version that's used to run this script. Exits immediately
34 # with a non-zero return code if it's lower than 4.4. Otherwise returns 0
36 # =============================================================================
37 check_bash_version
() {
38 message
="Error: Your Bash is too old. Please use Bash >= 4.4"
40 [[ "${BASH_VERSINFO[0]:-0}" -lt 4 ]] && echo $message && exit 1
43 if [[ "${BASH_VERSINFO[0]}" == 4 ]]; then
44 [[ "${BASH_VERSINFO[1]:-0}" -lt 4 ]] && echo $message && exit 1
50 # === parse_args ==============================================================
52 # Parse the input arguments passed to this script. Sets the global variables
53 # declared at the top.
56 # $1 - all input arguments
58 # Saved in the global variables for this script
59 # =============================================================================
62 while [ "${1:-}" != "" ]; do
63 # CASE 1: Compiler option
64 if [[ "${1:0:1}" == "-" ]] ; then
65 # Output file - extract it into a global variable
66 if [[ "$1" == "-o" ]] ; then
73 # Module directory - extract it into a global variable
74 if [[ "$1" == "-module-dir" ]]; then
81 # Intrinsics module dir - extract it into a global var
82 if [[ "$1" == "-intrinsics-module-directory" ]]; then shift
88 # Module suffix cannot be modified - this script defines it before
90 if [[ "$1" == "-module-suffix" ]]; then
91 echo "ERROR: \'-module-suffix\' is not available when using the \'flang\' script"
95 # Special treatment for `J <dir>` and `-I <dir>`. We translate these
96 # into `J<dir>` and `-I<dir>` respectively.
97 if [[ "$1" == "-J" ]] ||
[[ "$1" == "-I" ]]; then
105 # This is a regular option - just add it to the list.
107 if [[ $1 == "-c" ]]; then
111 if [[ $1 == "-E" ]]; then
112 PREPROCESS_ONLY
="True"
115 if [[ $1 == "-v" ||
$1 == "--version" ]]; then
122 # CASE 2: A regular file (either source or a library file)
123 elif [[ -f "$1" ]]; then
129 # CASE 3: Unsupported
130 echo "ERROR: unrecognised option format: \`$1\`. Perhaps non-existent file?"
136 # === categorise_files ========================================================
138 # Categorises input files into:
139 # * Fortran source files (to be compiled)
140 # * library files (to be linked into the final executable)
143 # $1 - all input files to be categorised (array, name reference)
145 # $2 - Fortran source files extracted from $1 (array, name reference)
146 # $3 - other source files extracted from $1 (array, name reference)
147 # $4 - object files extracted from $1 (array, name reference)
148 # $5 - lib files extracted from $1 (array, name reference)
149 # =============================================================================
152 local -n -r all_files
=$1
153 local -n fortran_sources
=$2
154 local -n other_sources
=$3
158 for current_file
in "${all_files[@]}"; do
159 file_ext
=${current_file##*.}
160 if [[ $file_ext == "f" ]] ||
[[ $file_ext == "f90" ]] ||
161 [[ $file_ext == "f" ]] ||
[[ $file_ext == "F" ]] ||
[[ $file_ext == "ff" ]] ||
162 [[ $file_ext == "f90" ]] ||
[[ $file_ext == "F90" ]] ||
[[ $file_ext == "ff90" ]] ||
163 [[ $file_ext == "f95" ]] ||
[[ $file_ext == "F95" ]] ||
[[ $file_ext == "ff95" ]] ||
164 [[ $file_ext == "cuf" ]] ||
[[ $file_ext == "CUF" ]] ||
[[ $file_ext == "f18" ]] ||
165 [[ $file_ext == "F18" ]] ||
[[ $file_ext == "ff18" ]]; then
166 fortran_sources
+=($current_file)
167 elif [[ $file_ext == "a" ]] ||
[[ $file_ext == "so" ]]; then
168 libs
+=($current_file)
169 elif [[ $file_ext == "o" ]]; then
170 objects
+=($current_file)
172 other_sources
+=($current_file)
177 # === categorise_opts ==========================================================
179 # Categorises compiler options into options for:
180 # * the Flang driver (either new or the "throwaway" driver)
181 # * the external Fortran driver that will generate the code
182 # Most options accepted by Flang will be claimed by it. The only exceptions are
186 # $1 - all compiler options (array, name reference)
188 # $2 - compiler options for the Flang driver (array, name reference)
189 # $3 - compiler options for the external driver (array, name reference)
190 # =============================================================================
194 local -n flang_opts
=$2
197 for opt
in "${all_opts[@]}"; do
198 # These options are claimed by Flang, but should've been dealt with in parse_args.
199 if [[ $opt == "-module-dir" ]] ||
200 [[ $opt == "-o" ]] ||
201 [[ $opt == "-fintrinsic-modules-path" ]] ; then
202 echo "ERROR: $opt should've been fully processed by \`parse_args\`"
207 # The options claimed by Flang. This list needs to be compatible with
208 # what's supported by Flang's compiler driver (i.e. `flang-new`).
209 [[ $opt == "-cpp" ]] ||
210 [[ $opt =~ ^
-D.
* ]] ||
211 [[ $opt == "-E" ]] ||
212 [[ $opt == "-falternative-parameter-statement" ]] ||
213 [[ $opt == "-fbackslash" ]] ||
214 [[ $opt == "-fcolor-diagnostics" ]] ||
215 [[ $opt == "-fdefault-double-8" ]] ||
216 [[ $opt == "-fdefault-integer-8" ]] ||
217 [[ $opt == "-fdefault-real-8" ]] ||
218 [[ $opt == "-ffixed-form" ]] ||
219 [[ $opt =~ ^
-ffixed-line-length=.
* ]] ||
220 [[ $opt == "-ffree-form" ]] ||
221 [[ $opt == "-fimplicit-none" ]] ||
222 [[ $opt =~ ^
-finput-charset=.
* ]] ||
223 [[ $opt == "-flarge-sizes" ]] ||
224 [[ $opt == "-flogical-abbreviations" ]] ||
225 [[ $opt == "-fno-color-diagnostics" ]] ||
226 [[ $opt == "-fxor-operator" ]] ||
227 [[ $opt == "-help" ]] ||
228 [[ $opt == "-nocpp" ]] ||
229 [[ $opt == "-pedantic" ]] ||
230 [[ $opt =~ ^
-std=.
* ]] ||
231 [[ $opt =~ ^
-U.
* ]] ||
232 [[ $opt == "-Werror" ]]; then
235 # We translate the following into equivalents understood by `flang-new`
236 [[ $opt == "-Mfixed" ]] ||
[[ $opt == "-Mfree" ]]; then
239 flang_opts
+=("-ffixed-form")
243 flang_opts
+=("-ffree-form")
247 echo "ERROR: $opt has no equivalent in 'flang-new'"
252 # Options that are needed for both Flang and the external driver.
253 [[ $opt =~
-I.
* ]] ||
254 [[ $opt =~
-J.
* ]] ||
255 [[ $opt == "-fopenmp" ]] ||
256 [[ $opt == "-fopenacc" ]]; then
260 # All other options are claimed for the external driver.
266 # === get_external_fc_name ====================================================
268 # Returns the name of external Fortran compiler based on values of
269 # environment variables.
270 # =============================================================================
271 get_external_fc_name
() {
272 if [[ -v FLANG_FC
]]; then
274 elif [[ -v F18_FC
]]; then
275 # We support F18_FC for backwards compatibility.
282 # === preprocess ==============================================================
284 # Runs the preprocessing. Fortran files are preprocessed using Flang. Other
285 # files are preprocessed using the external Fortran compiler.
288 # $1 - Fortran source files (array, name reference)
289 # $2 - other source files (array, name reference)
290 # $3 - compiler flags (array, name reference)
291 # =============================================================================
293 local -n fortran_srcs
=$1
294 local -n other_srcs
=$2
297 local ext_fc
="$(get_external_fc_name)"
299 local -r wd
=$
(cd "$(dirname "$0")/.." && pwd)
301 # Use the provided output file name.
302 if [[ ! -z ${OUTPUT_FILE:+x} ]]; then
303 output_definition
="-o $OUTPUT_FILE"
306 # Preprocess fortran sources using Flang
307 for idx
in "${!fortran_srcs[@]}"; do
308 if ! "$wd/bin/flang-new" -E "${opts[@]}" "${fortran_srcs[$idx]}" ${output_definition:+$output_definition}
310 echo flang: in "$PWD", flang-new failed with exit status $status: "$wd/bin
/flang-new
" "${opts[@]}" "$@
" >&2
315 # Preprocess other sources using Flang
316 for idx in "${!other_srcs[@]}"; do
317 if ! $ext_fc -E "${opts[@]}" "${other_srcs[$idx]}" ${output_definition:+$output_definition}
319 echo flang
: in "$PWD", flang-new failed with
exit status
$status: "$wd/bin/flang-new" "${opts[@]}" "$@" >&2
325 # === get_relocatable_name ======================================================
326 # This method generates the name of the output file for the compilation phase
327 # (triggered with `-c`). If the user of this script is only interested in
328 # compilation (`flang -c`), use $OUTPUT_FILE provided that it was defined.
329 # Otherwise, use the usual heuristics:
330 # * file.f --> file.o
331 # * file.c --> file.o
334 # $1 - input source file for which to generate the output name
335 # =============================================================================
336 get_relocatable_name
() {
339 if [[ $COMPILE_ONLY == "True" ]] && [[ ! -z ${OUTPUT_FILE:+x} ]]; then
340 out_file
="$OUTPUT_FILE"
342 current_ext
=${src_file##*.}
345 out_file
=$
(basename "${src_file}" "$current_ext")${new_ext}
351 # === main ====================================================================
352 # Main entry point for this script
353 # =============================================================================
358 if [[ $PRINT_VERSION == "True" ]]; then
359 echo "flang version @FLANG_VERSION@"
363 # Source, object and library files provided by the user
364 local fortran_source_files
=()
365 local other_source_files
=()
366 local object_files
=()
368 categorise_files INPUT_FILES fortran_source_files other_source_files object_files lib_files
370 if [[ $PREPROCESS_ONLY == "True" ]]; then
371 preprocess fortran_source_files other_source_files OPTIONS
375 # Options for the Flang driver.
376 # NOTE: We need `-fc1` to make sure that the frontend driver rather than
377 # compiler driver is used. We also need to make sure that that's the first
378 # flag that the driver will see (otherwise it assumes compiler/toolchain
380 local flang_options
=("-fc1")
381 # Options for the external Fortran Compiler
382 local ext_fc_options
=()
383 categorise_opts OPTIONS flang_options ext_fc_options
385 local -r wd
=$
(cd "$(dirname "$0")/.." && pwd)
387 # uuidgen is common but not installed by default on some distros
388 if ! command -v uuidgen
&> /dev
/null
390 echo "uuidgen is required for generating unparsed file names."
395 # Base-name for the unparsed files. These are just temporary files that are
396 # first generated and then deleted by this script.
397 # NOTE: We need to make sure that the base-name is unique to every
398 # invocation. Otherwise we can't use this script in parallel.
399 local -r unique_id
=$
(uuidgen | cut
-b25-36)
400 local -r unparsed_file_base
="flang_unparsed_file_$unique_id"
402 flang_options
+=("-module-suffix")
403 flang_options
+=(".f18.mod")
404 flang_options
+=("-fdebug-unparse")
405 flang_options
+=("-fno-analyzed-objects-for-unparse")
407 [[ ! -z ${MODULE_DIR} ]] && flang_options
+=("-module-dir ${MODULE_DIR}")
408 [[ ! -z ${INTRINSICS_MOD_DIR} ]] && flang_options
+=("-intrinsics-module-directory ${INTRINSICS_MOD_DIR}")
409 for idx
in "${!fortran_source_files[@]}"; do
411 "$wd/bin/flang-new" "${flang_options[@]}" "${fortran_source_files[$idx]}" -o "${unparsed_file_base}_${idx}.f90"
414 if [[ $ret_status != 0 ]]; then
415 echo flang
: in "$PWD", flang-new failed with
exit status
"$ret_status": "$wd/bin/flang-new" "${flang_options[@]}" "$@" >&2
420 # STEP 2: Compile Fortran Source Files
421 local ext_fc
="$(get_external_fc_name)"
422 # Temporary object files generated by this script. To be deleted at the end.
423 local temp_object_files
=()
424 for idx
in "${!fortran_source_files[@]}"; do
425 # We always have to specify the output name with `-o <out_obj_file>`. This
426 # is because we are using the unparsed rather than the original source file
427 # below. As a result, we cannot rely on the compiler-generated output name.
428 out_obj_file
=$
(get_relocatable_name
"${fortran_source_files[$idx]}")
431 $ext_fc "-c" "${ext_fc_options[@]}" "${unparsed_file_base}_${idx}.f90" "-o" "${out_obj_file}"
434 if [[ $ret_status != 0 ]]; then
435 echo flang
: in "$PWD", "$ext_fc" failed with
exit status
"$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
438 temp_object_files
+=(${out_obj_file})
441 # Delete the unparsed files
442 for idx
in "${!fortran_source_files[@]}"; do
443 rm "${unparsed_file_base}_${idx}.f90"
446 # STEP 3: Compile Other Source Files
447 for idx
in "${!other_source_files[@]}"; do
448 # We always specify the output name with `-o <out_obj_file>`. The user
449 # might have used `-o`, but we never add it to $OPTIONS (or
450 # $ext_fc_options). Hence we need to use `get_relocatable_name`.
451 out_obj_file
=$
(get_relocatable_name
"${other_source_files[$idx]}")
454 $ext_fc "-c" "${ext_fc_options[@]}" "${other_source_files[${idx}]}" "-o" "${out_obj_file}"
457 if [[ $ret_status != 0 ]]; then
458 echo flang
: in "$PWD", "$ext_fc" failed with
exit status
"$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@" >&2
461 temp_object_files
+=(${out_obj_file})
465 if [[ $COMPILE_ONLY == "True" ]]; then
469 if [[ ${#temp_object_files[@]} -ge 1 ]] ||
[[ ${#object_files[@]} -ge 1 ]] ; then
470 # If $OUTPUT_FILE was specified, use it for the output name.
471 if [[ ! -z ${OUTPUT_FILE:+x} ]]; then
472 output_definition
="-o $OUTPUT_FILE"
478 $ext_fc "${ext_fc_options[@]}" "${object_files[@]}" "${temp_object_files[@]}" "${lib_files[@]}" ${output_definition:+$output_definition}
481 if [[ $ret_status != 0 ]]; then
482 echo flang: in "$PWD", "$ext_fc" failed with exit status "$ret_status": "$ext_fc" "${ext_fc_options[@]}" "$@
" >&2
487 # Delete intermediate object files
488 for idx in "${!fortran_source_files[@]}"; do
489 rm "${temp_object_files[$idx]}"