[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / flang / tools / f18 / flang-to-external-fc.in
blob24ceda3b7812cf5128aa12f0541e80d84bbe3fb8
1 #! /usr/bin/env bash
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
11 # will:
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 #===------------------------------------------------------------------------===#
19 set -euo pipefail
21 # Global variables to make the parsing of input arguments a bit easier
22 INPUT_FILES=()
23 OPTIONS=()
24 OUTPUT_FILE=""
25 MODULE_DIR=""
26 INTRINSICS_MOD_DIR=""
27 COMPILE_ONLY="False"
28 PREPROCESS_ONLY="False"
29 PRINT_VERSION="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
35 # (success).
36 # =============================================================================
37 check_bash_version() {
38 message="Error: Your Bash is too old. Please use Bash >= 4.4"
39 # Major version
40 [[ "${BASH_VERSINFO[0]:-0}" -lt 4 ]] && echo $message && exit 1
42 # Minor version
43 if [[ "${BASH_VERSINFO[0]}" == 4 ]]; then
44 [[ "${BASH_VERSINFO[1]:-0}" -lt 4 ]] && echo $message && exit 1
47 return 0
50 # === parse_args ==============================================================
52 # Parse the input arguments passed to this script. Sets the global variables
53 # declared at the top.
55 # INPUTS:
56 # $1 - all input arguments
57 # OUTPUTS:
58 # Saved in the global variables for this script
59 # =============================================================================
60 parse_args()
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
67 shift
68 OUTPUT_FILE="$1"
69 shift
70 continue
73 # Module directory - extract it into a global variable
74 if [[ "$1" == "-module-dir" ]]; then
75 shift
76 MODULE_DIR="$1"
77 shift
78 continue
81 # Intrinsics module dir - extract it into a global var
82 if [[ "$1" == "-intrinsics-module-directory" ]]; then shift
83 INTRINSICS_MOD_DIR=$1
84 shift
85 continue
88 # Module suffix cannot be modified - this script defines it before
89 # calling the driver.
90 if [[ "$1" == "-module-suffix" ]]; then
91 echo "ERROR: \'-module-suffix\' is not available when using the \'flang\' script"
92 exit 1
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
98 opt=$1
99 shift
100 OPTIONS+=("$opt$1")
101 shift
102 continue
105 # This is a regular option - just add it to the list.
106 OPTIONS+=($1)
107 if [[ $1 == "-c" ]]; then
108 COMPILE_ONLY="True"
111 if [[ $1 == "-E" ]]; then
112 PREPROCESS_ONLY="True"
115 if [[ $1 == "-v" || $1 == "--version" ]]; then
116 PRINT_VERSION="True"
119 shift
120 continue
122 # CASE 2: A regular file (either source or a library file)
123 elif [[ -f "$1" ]]; then
124 INPUT_FILES+=($1)
125 shift
126 continue
128 else
129 # CASE 3: Unsupported
130 echo "ERROR: unrecognised option format: \`$1\`. Perhaps non-existent file?"
131 exit 1
133 done
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)
142 # INPUTS:
143 # $1 - all input files to be categorised (array, name reference)
144 # OUTPUTS:
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 # =============================================================================
150 categorise_files()
152 local -n -r all_files=$1
153 local -n fortran_sources=$2
154 local -n other_sources=$3
155 local -n objects=$4
156 local -n libs=$5
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)
171 else
172 other_sources+=($current_file)
174 done
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
183 # `-I` and `-J`.
185 # INPUTS:
186 # $1 - all compiler options (array, name reference)
187 # OUTPUTS:
188 # $2 - compiler options for the Flang driver (array, name reference)
189 # $3 - compiler options for the external driver (array, name reference)
190 # =============================================================================
191 categorise_opts()
193 local -n all_opts=$1
194 local -n flang_opts=$2
195 local -n fc_opts=$3
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\`"
203 exit 1
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
233 flang_opts+=($opt)
234 elif
235 # We translate the following into equivalents understood by `flang-new`
236 [[ $opt == "-Mfixed" ]] || [[ $opt == "-Mfree" ]]; then
237 case $opt in
238 -Mfixed)
239 flang_opts+=("-ffixed-form")
242 -Mfree)
243 flang_opts+=("-ffree-form")
247 echo "ERROR: $opt has no equivalent in 'flang-new'"
248 exit 1
250 esac
251 elif
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
257 flang_opts+=($opt)
258 fc_opts+=($opt)
259 else
260 # All other options are claimed for the external driver.
261 fc_opts+=($opt)
263 done
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
273 echo ${FLANG_FC}
274 elif [[ -v F18_FC ]]; then
275 # We support F18_FC for backwards compatibility.
276 echo ${F18_FC}
277 else
278 echo gfortran
282 # === preprocess ==============================================================
284 # Runs the preprocessing. Fortran files are preprocessed using Flang. Other
285 # files are preprocessed using the external Fortran compiler.
287 # INPUTS:
288 # $1 - Fortran source files (array, name reference)
289 # $2 - other source files (array, name reference)
290 # $3 - compiler flags (array, name reference)
291 # =============================================================================
292 preprocess() {
293 local -n fortran_srcs=$1
294 local -n other_srcs=$2
295 local -n opts=$3
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}
309 then status=$?
310 echo flang: in "$PWD", flang-new failed with exit status $status: "$wd/bin/flang-new" "${opts[@]}" "$@" >&2
311 exit $status
313 done
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}
318 then status=$?
319 echo flang: in "$PWD", flang-new failed with exit status $status: "$wd/bin/flang-new" "${opts[@]}" "$@" >&2
320 exit $status
322 done
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
333 # INPUTS:
334 # $1 - input source file for which to generate the output name
335 # =============================================================================
336 get_relocatable_name() {
337 local -r src_file=$1
339 if [[ $COMPILE_ONLY == "True" ]] && [[ ! -z ${OUTPUT_FILE:+x} ]]; then
340 out_file="$OUTPUT_FILE"
341 else
342 current_ext=${src_file##*.}
343 new_ext="o"
345 out_file=$(basename "${src_file}" "$current_ext")${new_ext}
348 echo "$out_file"
351 # === main ====================================================================
352 # Main entry point for this script
353 # =============================================================================
354 main() {
355 check_bash_version
356 parse_args "$@"
358 if [[ $PRINT_VERSION == "True" ]]; then
359 echo "flang version @FLANG_VERSION@"
360 exit 0
363 # Source, object and library files provided by the user
364 local fortran_source_files=()
365 local other_source_files=()
366 local object_files=()
367 local lib_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
372 exit 0
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
379 # driver mode).
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
389 then
390 echo "uuidgen is required for generating unparsed file names."
391 exit 1
394 # STEP 1: Unparse
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
410 set +e
411 "$wd/bin/flang-new" "${flang_options[@]}" "${fortran_source_files[$idx]}" -o "${unparsed_file_base}_${idx}.f90"
412 ret_status=$?
413 set -e
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
416 exit "$ret_status"
418 done
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]}")
430 set +e
431 $ext_fc "-c" "${ext_fc_options[@]}" "${unparsed_file_base}_${idx}.f90" "-o" "${out_obj_file}"
432 ret_status=$?
433 set -e
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
436 exit "$ret_status"
438 temp_object_files+=(${out_obj_file})
439 done
441 # Delete the unparsed files
442 for idx in "${!fortran_source_files[@]}"; do
443 rm "${unparsed_file_base}_${idx}.f90"
444 done
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]}")
453 set +e
454 $ext_fc "-c" "${ext_fc_options[@]}" "${other_source_files[${idx}]}" "-o" "${out_obj_file}"
455 ret_status=$?
456 set -e
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
459 exit "$ret_status"
461 temp_object_files+=(${out_obj_file})
462 done
464 # STEP 4: Link
465 if [[ $COMPILE_ONLY == "True" ]]; then
466 exit 0;
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"
473 else
474 output_definition=""
477 set +e
478 $ext_fc "${ext_fc_options[@]}" "${object_files[@]}" "${temp_object_files[@]}" "${lib_files[@]}" ${output_definition:+$output_definition}
479 ret_status=$?
480 set -e
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
483 exit "$ret_status"
487 # Delete intermediate object files
488 for idx in "${!fortran_source_files[@]}"; do
489 rm "${temp_object_files[$idx]}"
490 done
493 main "${@}"