Create single-file distribution of a program (like FPM)

So fpm can be very conveniently obtained and built using a one-liner:

curl -L https://github.com/fortran-lang/fpm/releases/download/v0.10.0/fpm-0.10.0.F90 \
-o fpm.F90 && gfortran -O0 fpm.F90 -o fpm && install fpm /usr/local/bin/

How can I build a similar one-file megasource of my Fortran application?

1 Like

I used a script like the following

#!/usr/bin/env bash

output="build/single_file/my_prog.F90"

args=("$@")
file=$(printf "%s\n" "${args[@]}" | grep -E '^.+\.[fF]90$')
if [ $? = 0 ]; then
  echo " + Appending source file '$file' to '${output}'"
  cat $file >> "${output}"
  echo "" >> "${output}"
fi
exec gfortran $@

And then used that script as the “compiler” when running fpm. I.e.

#!/usr/bin/env bash

rm -rf build/file_combiner*
mkdir -p build/single_file
echo "! single file program" > build/single_file/my_prog.F90
fpm build --compiler file_combiner_gfortran.sh
echo "Compiling single file version..."
gfortran -J build/single_file -o build/single_file/my_prog build/single_file/my_prog.F90 $@
echo "Single file compilation complete"%              

It’s a tad hacky, but it works.

4 Likes

Hacky but awesome. Thank you a lot!! Sometimes it is much easier to transfer one file and compile it, or hand it to your coworker who is not familiar with Fortran tooling or cmake. Free intra-procedural optimizations are a bonus.

It needn’t be complicated if you have no “include” statements::

cat *.f  *.f90 >onefile.f90
gfortran onefile.f90

I send Fortran source code to users with no Fortran, git or make expereince, and this is much easier for them (and for me). Adding “knowledge dependencies” will reduce adoption. It occurs to me that if you do have include statements, changing them to #include, and invoking the c preporcessor might work. I haven’t tried that.

This approach would not take care of module dependencies, I think.

Unfortunately some of my codes do have the “include” statements, but I think I should be able to patch the script to handle them.

Thank you very much :slight_smile: Simplicity is the power!

(At least IME) order matters if you have modules.

I do miss pre-modules/explicit-interface simplicity of compilation order but the script above not only uses fpm to put them in the
right order, it will also include the source of dependencies. If you
know you have gfortran and fpm you can replace the “cat” with

OPS=$(sed -e 's/ -o .*/ /' <<<"$*")
/usr/bin/gfortran -E -cpp -Wp,-P -ffree-line-length-none $OPS

and that will allow for #include statements as well. Unfortunately, -E does not expand Fortran INCLUDE statements so you would have to change them. Using gfortran instead of cat works well instead of a simple call to cpp because the -I options are easier to just pass to gfortran versus extracting them to add to the cpp command.

This appears to mix fixed-form and free-form source code into the single file. Can any fortran compiler actually handle that?

1 Like

I do not know of any that auto-sense it, but several allow it with custom directives. For example, Cray:

https://support.hpe.com/hpesc/public/docDisplay?docId=a00113907en_us&page=FREE.html&docLocale=en_US

There are several commericial and open_source tools for changing fixed-format to free-format that could solve that. The latest versions of fpm default to assuming all code is free-format by default, as one example where files ending in .f might be free-format already as well.

I hadn’t thought about how something like this might be useful from a purely portable standpoint, i.e. sharing code!

I’ve implemented this feature in Simply Fortran’s development version now, and it should appear as an Export option in the next release. It handles dependency ordering and INCLUDE statements properly.

4 Likes