diff --git a/learn/building_programs/build_tools.md b/learn/building_programs/build_tools.md index fa1d65c3e..00670e893 100644 --- a/learn/building_programs/build_tools.md +++ b/learn/building_programs/build_tools.md @@ -4,75 +4,555 @@ title: Build tools permalink: /learn/building_programs/build_tools --- -If this seems complicated, well, you are right and we are only -scratching the surface here. The complications arise because of -differences between platforms, differences between compilers/linkers and -because of differences in the way programs are set up. Fortunately, -there are many tools to help configure and maintain the build steps. -We will not try and catalogue them, but give instead a very limited -list of tools that you typically encounter: - -* The `make` utility is a classical tool that uses instructions about -how the various components of a program depend on each other to -efficiently compile and link the program (or programs). It takes a -so-called `Makefile` that contains the dependencies. - -Simply put: - -If a program file is older than any of the libraries and object files -it depends on, the make utility knows it has to rebuild it and goes on -to look at the libraries and object files - are any out of date? - -If an object file is older than the corresponding source file, the -make utility knows it has to compile the source file. - -* Integrated development tools take care of many of the above details. A -popular cross-platform tool is Microsoft's [Visual Studio Code](https://code.visualstudio.com/), but others exist, -such as [Atom](https://atom.io/), [Eclipse Photran](https://www.eclipse.org/photran/), and [Code::Blocks](http://www.codeblocks.org/). They offer a graphical -user-interface, but are often very specific for the compiler and -platform. - -* Maintenance tools like autotools and CMake can generate Makefiles or +Compiling your Fortran projects by hand can become quite complicated depending +on the number of source files and the interdependencies through the module. +Supporting different compilers and linkers or different platforms can become +increasingly complicated unless the right tools are used to automatically +perform those tasks. + +Depending on the size of your project and the purpose of project different +options for the build automation can be used. + +First, your integrated development environment probably provides a way to build +your program. A popular cross-platform tool is Microsoft's +Visual Studio Code, +but others exist, such as +Atom, +Eclipse Photran, +and Code::Blocks. +They offer a graphical user-interface, but are often very specific for the +compiler and platform. + +For smaller projects, the rule based build system ``make`` is a common +choice. Based on the rules defined it can perform task like (re)compiling +object files from updated source files, creating libraries and linking +executables. +To use ``make`` for your project you have to encode those rules in ``Makefile``, +which defines the interdependencies of all the final program, the intermediary +object files or libraries and the actual source files. +For a short introduction see [the guide on ``make``](#using-make-as-build-tool). + +Maintenance tools like autotools and CMake can generate Makefiles or Visual Studio project files via a high-level description. They abstract away from the compiler and platform specifics. -Here is a very simple example of a `Makefile` as used by the `make` utility, -just to give you an impression: +Which of those tools are the best choice for your projects depends on many factors. +Choose a build tool you are comfortable working with, it should not get in your +way while developing. Spending more time on working against your build tools +than doing actual development work can quickly become frustrating. + +Also, consider the accessibility of your build tools. +If it is restricted to a specific integrated development environment, can all +developers on your project access it? +If you are using a specific build system, does it work on all platforms you +are developing for? +How large is the entry barrier of your build tools? Consider the learning curve +for the build tools, the perfect build tool will be of no use, if you have to +learn a complex programming language first to add a new source file. +Finally, consider what other project are using, those you are depending on and +those that use (or will use) your project as dependency. + + +## Using make as build tool + +The most well-known and commonly used build system is called ``make``. +It performs actions following rules defined in a configuration file +called ``Makefile`` or ``makefile``, which usually leads to compiling a program +from the provided source code. + +{% include tip.html + content="For an in-depth ``make`` tutorial lookup its info page. There is an online version of this info page, available." +%} +We will start with the basics from your clean source directory. Create and open +the file ``Makefile``, we start with a simple rule called *all*: + +```make +all: + echo "$@" ``` -# Collect the macros at the beginning - easier to customise -FC = gfortran -LD = gfortran -FCOPTS = -c -LDOPTS = "-o " -EXE = .exe -OBJ = .o +After saving the ``Makefile`` run it by executing ``make`` in the same directory. +You should see the following output: + + echo "all" + all + +First, we note that ``make`` is substituting ``$@`` for the name of the rule, +the second thing to note is that ``make`` is always printing the command it is +running, finally, we see the result of running ``echo "all"``. + +{% include note.html + content="We call the entry point of our ``Makefile`` always *all* by convention, but you can choose whatever name you like." +%} -all: tabulate$(EXE) +{% capture note %} -tabulate$(EXE) : tabulate$(OBJ) function$(OBJ) -{tab}$(LD) $(LDOPTS)tabulate$(EXE) tabulate.f90 function$(OBJ) +You should not have noticed it if your editor is working correctly, +but you have to indent the content of a rule with a tab character. +In case you have problems running the above ``Makefile`` and see an error like -tabulate$(OBJ) : tabulate.f90 function.mod -{tab}$(FC) $(FCOPTS) tabulate.f90 + Makefile:2: *** missing separator. Stop. -function$(OBJ) : function.f90 -{tab}$(FC) $(FCOPTS) function.f90 +The indentation is probably not correct. In this case replace the indentation +in the second line with a tab character. -(A peculiarity of `make` is that in the input file, tab characters are used -in several places - here indicated as "{tab}" - as significant whitespace.) +{% endcapture %} +{% include note.html title="Note" content=note %} -When stored in a file "Makefile" and "{tab}" replaced by a tab character, -you can run it like: +Now we want to make our rules more complicated, therefore we add another rule: -```shell -$ make +```make +PROG := my_prog + +all: $(PROG) + echo "$@ depends on $^" + +$(PROG): + echo "$@" ``` -(the name `Makefile` is the default, otherwise use the option `-f` to specify -a different file name). Now only change the file "tabulate.f90" and run it -again. You will see that only that file gets compiled again and then the -program is built. The file "function.f90" was not changed, so the object -file and the module intermediate file would remain unchanged, so there -is no need to recompile it. +Note how we declare variables in ``make``, you should always declare your local +variables with ``:=``. To access the content of a variable we use the ``$(...)``, +note that we have to enclose the variable name in parenthesis. + +{% capture note %} + +The declaration of variables is usually done with ``:=``, but ``make`` does +support *recursively expanded* variables as well with ``=``. +Normally, the first kind of declaration is wanted, as they are more predictable +and do not have a runtime overhead from the recursive expansion. +{% endcapture %} +{% include note.html title="Note" content=note %} + +We introduced a dependency of the rule all, namely the content of the variable +``PROG``, also we modified the printout, we want to see all the dependencies +of this rule, which are stored in the variable ``$^``. +Now for the new rule which we name after the value of the variable ``PROG``, +it does the same thing we did before for the rule *all*, note how the value +of ``$@`` is dependent on the rule it is used in. + +Again check by running the ``make``, you should see: + + echo "my_prog" + my_prog + echo "all depends on my_prog" + all depends on my_prog + +The dependency has been correctly resolved and evaluated before performing +any action on the rule *all*. +Let's run only the second rule: type ``make my_prog`` and you will only find +the first two lines in your terminal. + +The next step is to perform some real actions with ``make``, we take +the source code from the previous chapter here and add new rules to our +``Makefile``: + +```make +OBJS := tabulate.o functions.o +PROG := my_prog + +all: $(PROG) + +$(PROG): $(OBJS) + gfortran -o $@ $^ + +$(OBJS): %.o: %.f90 + gfortran -c -o $@ $< +``` + +We define ``OBJS`` which stands for object files, our program depends on +those ``OBJS`` and for each object file we create a rule to make them from +a source file. +The last rule we introduced is a pattern matching rule, ``%`` is the common +pattern between ``tabulate.o`` and ``tabulate.f90``, which connects our object file +``tabulate.o`` to the source file ``tabulate.f90``. +With this set, we run our compiler, here ``gfortran`` and translate the source +file into an object file, we do not create an executable yet due to the ``-c`` +flag. +Note the usage of the ``$<`` for the first element of the dependencies here. + +After compiling all the object files we attempt to link the program, we do not +use a linker directly, but ``gfortran`` to produce the executable. + +Now we run the build script with ``make``: + + gfortran -c -o tabulate.o tabulate.f90 + tabulate.f90:2:7: + + 2 | use user_functions + | 1 + Fatal Error: Cannot open module file ‘user_functions.mod’ for reading at (1): No such file or directory + compilation terminated. + make: *** [Makefile:10: tabulate.f90.o] Error 1 + +We remember that we have dependencies between our source files, therefore we add +this dependency explicitly to the ``Makefile`` with + +```make +tabulate.o: functions.o +``` + +Now we can retry and find that the build is working correctly. The output should +look like + + gfortran -c -o functions.o functions.f90 + gfortran -c -o tabulate.o tabulate.f90 + gfortran -o my_prog tabulate.o functions.o + +You should find *four* new files in the directory now. +Run ``my_prog`` to make sure everything works as expected. +Let's run ``make`` again: + + make: Nothing to be done for 'all'. + +Using the timestamps of the executable ``make`` was able to determine, it is +newer than both ``tabulate.o`` and ``functions.o``, which in turn are newer than +``tabulate.f90`` and ``functions.f90``. +Therefore, the program is already up-to-date with the latest code and no +action has to be performed. + +In the end, we will have a look at a complete ``Makefile``. + +```make +# Disable all of make's built-in rules (similar to Fortran's implicit none) +MAKEFLAGS += --no-builtin-rules --no-builtin-variables +# configuration +FC := gfortran +LD := $(FC) +RM := rm -f +# list of all source files +SRCS := tabulate.f90 functions.f90 +PROG := my_prog + +OBJS := $(addsuffix .o, $(SRCS)) + +.PHONY: all clean +all: $(PROG) + +$(PROG): $(OBJS) + $(LD) -o $@ $^ + +$(OBJS): %.o: % + $(FC) -c -o $@ $< + +# define dependencies between object files +tabulate.f90.o: functions.f90.o user_functions.mod + +# rebuild all object files in case this Makefile changes +$(OBJS): $(MAKEFILE_LIST) + +clean: + $(RM) $(filter %.o, $(OBJS)) $(wildcard *.mod) $(PROG) +``` + +Since you are starting with ``make`` we highly recommend to always include +the first line, like with Fortran's ``implicit none`` we do not want to have +implicit rules messing up our ``Makefile`` in surprising and harmful ways. + +Next, we have a configuration section where we define variables, in case you +want to switch out your compiler, it can be easily done here. +We also introduced the ``SRCS`` variable to hold all source files, which is +more intuitive than specifying object files. +We can easily create the object files by appending a ``.o`` suffix using the +functions ``addsuffix``. +The ``.PHONY`` is a special rule, which should be used for all entry points +of your ``Makefile``, here we define two entry point, we already know *all*, +the new *clean* rule deletes all the build artifacts again such that we indeed +start with a clean directory. + +Also, we slightly changed the build rule for the object files to account for +appending the ``.o`` suffix instead of substituting it. +Notice that we still need to explicitly define the interdependencies in the +``Makefile``. We also added a dependency for the object files on the ``Makefile`` +itself, in case you change the compiler, this will allow you to safely rebuild. + +Now you know enough about ``make`` to use it for building small projects. +If you plan to use ``make`` more extensively, we have compiled a few tips +for you as well. + +{% capture note %} + +In this guide, we avoided and disabled a lot of the commonly used ``make`` +features that can be particularly troublesome if not used correctly, we highly +recommend staying away from the builtin rules and variables if you do not feel +confident working with ``make``, but explicitly declare all variables and rules. + +You will find that ``make`` is capable tool to automate short interdependent +workflows and to build small projects. But for larger projects, you will +probably soon run against some of it limitations. Usually, ``make`` is therefore +not used alone but combined with other tools to generate the ``Makefile`` +completely or in parts. +{% endcapture %} +{% include note.html title="Note" content=note %} + + +### Recursively expanded variables + +Commonly seen in many projects are recursively expanded variables (declared with +``=`` instead of ``:=``). Recursive expansion of your variables allows out-of-order +declaration and other neat tricks with ``make``, since they are defined as rules, +which are expanded at runtime, rather than being defined while parsing. + +For example, declaring and using your Fortran flags with this snippet will work +completely fine: + +```make +all: + echo $(FFLAGS) + +FFLAGS = $(include_dirs) -O +include_dirs += -I./include +include_dirs += -I/opt/some_dep/include +``` + +You should find the expected (or maybe unexpected) printout after running ``make`` + + echo -I./include -I/opt/some_dep/include -O + -I./include -I/opt/some_dep/include -O + +{% include note.html content="appending with ``+=`` to an undefined variable will produce a recursively expanded variable with this state being inherited for all further appending." %} + +While, it seems like an interesting feature to use, it tends to lead to +surprising and unexpected outcomes. Usually, when defining variables like your +compiler, there is little reason to actually use the recursive expansion at all. + +The same can easily be archived using the ``:=`` declaration: + +```make +all: + echo $(FFLAGS) + +include_dirs := -I./include +include_dirs += -I/opt/some_dep/include +FFLAGS := $(include_dirs) -O +``` + +{% include important.html content="always think of a ``Makefile`` as a whole set of rules, it must be parsed completely before any rule can be evaluated." %} + +You can use whatever kind of variables you like most, mixing them should be done +carefully, of course. It is important to be aware of the differences between the +two kinds and the respective implications. + + +### Comments and whitespace + +There are some caveats with whitespace and comments, which might pop up from +time to time when using ``make``. First, ``make`` does not know of any data +type except for strings and the default separator is just a space. +This means ``make`` will give a hard time trying to build a project which +has spaces in file names. If you encounter such case, renaming the file +is possibly the easiest solution at hand. + +Another common problem is leading and trailing whitespace, once introduced, +``make`` will happily carry it along and it does in fact make a difference +when comparing strings in ``make``. + +Those can be introduced by comments like + +```make +prefix := /usr # path to install location +install: + echo "$(prefix)/lib" +``` + +While the comment will be correctly removed by ``make``, the trailing two spaces +are now part of the variable content. Run ``make`` and check that this is indeed +the case: + +``` +echo "/usr /lib" +/usr /lib +``` + +To solve this issue, you can either move the comment, or strip the whitespace with +the ``strip`` function instead. Alternatively, you could try to ``join`` the +strings. + +```make +prefix := /usr # path to install location +install: + echo "$(strip $(prefix))/lib" + echo "$(join $(join $(prefix), /), lib)" +``` + +All in all, none of this solutions will make your ``Makefile`` more readable, +therefore, it is prudent to pay extra attention to whitespace and comments when +writing and using ``make``. + + +## The meson build system + +After you have learned the basics of ``make``, which we call a low-level build +system, we will introduce ``meson``, a high-level build system. +While you specify in a low-level build system how to build your program, +you can use a high-level build system to specify what to build. +A high-level build system will deal for you with how and generate +build files for a low-level build system. + +There are plenty of high-level build systems available, but we will focus on +``meson`` because it is constructed to be particularly user friendly. +The default low-level build-system of ``meson`` is called ``ninja``. + +Let's have a look at a complete ``meson.build`` file: + + +```python +project('my_proj', 'fortran', meson_version: '>=0.49') +executable('my_prog', files('tabulate.f90', 'functions.f90')) +``` + +And we are already done, the next step is to configure our low-level build system +with ``meson setup build``, you should see output somewhat similar to this + + The Meson build system + Version: 0.53.2 + Source dir: /home/awvwgk/Examples + Build dir: /home/awvwgk/Examples/build + Build type: native build + Project name: my_proj + Project version: undefined + Fortran compiler for the host machine: gfortran (gcc 9.2.1 "GNU Fortran (Arch Linux 9.2.1+20200130-2) 9.2.1 20200130") + Fortran linker for the host machine: gfortran ld.bfd 2.34 + Host machine cpu family: x86_64 + Host machine cpu: x86_64 + Build targets in project: 1 + + Found ninja-1.10.0 at /usr/bin/ninja + +The provided information at this point is already more detailed than anything +we could have provided in a ``Makefile``, let's run the build with +``ninja -C build``, which should show something like + + [1/4] Compiling Fortran object 'my_prog@exe/functions.f90.o'. + [2/4] Dep hack + [3/4] Compiling Fortran object 'my_prog@exe/tabulate.f90.o'. + [4/4] Linking target my_prog. + +Find and test your program at ``build/my_prog`` to ensure it works correctly. +We note the steps ``ninja`` performed are the same we would have coded up in a +``Makefile`` (including the dependency), yet we did not have to specify them, +have a look at your ``meson.build`` file again: + +```python +project('my_proj', 'fortran', meson_version: '>=0.49') +executable('my_prog', files('tabulate.f90', 'functions.f90')) +``` + +We only specified that we have a Fortran project (which happens to require +a certain version of ``meson`` for the Fortran support) and told ``meson`` +to build an executable ``my_prog`` from the files ``tabulate.f90`` and +``functions.f90``. +We had not to tell ``meson`` how to build the project, it figured this out +by itself. + +{% capture note %} + +``meson`` is a cross-platform build system, the project you just specified +for your program can be used to compile binaries for your native operating +system or to cross-compile your project for other platforms. +Similarly, the ``meson.build`` file is portable and will work on different +platforms as well. +{% endcapture %} +{% include note.html title="Note" content=note %} + +The documentation of ``meson`` can be found at the +meson-build webpage. + + +## Creating a CMake project + +Similar to ``meson`` CMake is a high-level build system as well and commonly +used to build Fortran projects. + +{% capture note %} + +CMake follows a slightly different strategy and provides you with a complete +programming language to create your build files. +This is has the advantage that you can do almost everything with CMake, +but your CMake build files can also become as complex as the program you are +building. +{% endcapture %} +{% include note.html title="Note" content=note %} + +Start by creating the file ``CMakeLists.txt`` with the content + +```cmake +cmake_minimum_required(VERSION 3.7) +project("my_proj" LANGUAGES "Fortran") +add_executable("my_prog" "tabulate.f90" "functions.f90") +``` + +Similar to ``meson`` we are already done with our CMake build file. +We configure our low-level build files with ``cmake -B build -G Ninja``, +you should see output similar to this + + -- The Fortran compiler identification is GNU 10.2.0 + -- Detecting Fortran compiler ABI info + -- Detecting Fortran compiler ABI info - done + -- Check for working Fortran compiler: /usr/bin/f95 - skipped + -- Checking whether /usr/bin/f95 supports Fortran 90 + -- Checking whether /usr/bin/f95 supports Fortran 90 - yes + -- Configuring done + -- Generating done + -- Build files have been written to: /home/awvwgk/Examples/build + +You might be surprised that CMake tries to use the compiler ``f95``, fortunately +this is just a symbolic link to ``gfortran`` on most systems and not the actual +``f95`` compiler. +To give CMake a better hint you can export the environment variable ``FC=gfortran`` +rerunning should show the correct compiler name now + + -- The Fortran compiler identification is GNU 10.2.0 + -- Detecting Fortran compiler ABI info + -- Detecting Fortran compiler ABI info - done + -- Check for working Fortran compiler: /usr/bin/gfortran - skipped + -- Checking whether /usr/bin/gfortran supports Fortran 90 + -- Checking whether /usr/bin/gfortran supports Fortran 90 - yes + -- Configuring done + -- Generating done + -- Build files have been written to: /home/awvwgk/Example/build + +In a similar manner you could use your Intel Fortran compiler instead to build +your project (set ``FC=ifort``). + +CMake provides support for several low-level build files, since the default is +platform specific, we will just use ``ninja`` since we already used it together +with ``meson``. As before, build your project with ``ninja -C build``: + + [1/6] Building Fortran preprocessed CMakeFiles/my_prog.dir/functions.f90-pp.f90 + [2/6] Building Fortran preprocessed CMakeFiles/my_prog.dir/tabulate.f90-pp.f90 + [3/6] Generating Fortran dyndep file CMakeFiles/my_prog.dir/Fortran.dd + [4/6] Building Fortran object CMakeFiles/my_prog.dir/functions.f90.o + [5/6] Building Fortran object CMakeFiles/my_prog.dir/tabulate.f90.o + [6/6] Linking Fortran executable my_prog + +Find and test your program at ``build/my_prog`` to ensure it works correctly. +The steps ``ninja`` performed are somewhat different, because there is usually +more than one way to write the low-level build files to accomplish the task +of building a project. Fortunately, we do not have to concern ourselves but have +our build system handle those details for us. + +Finally, we will shortly recap on our complete ``CMakeLists.txt`` to specify +our project: + +```cmake +cmake_minimum_required(VERSION 3.7) +project("my_proj" LANGUAGES "Fortran") +add_executable("my_prog" "tabulate.f90" "functions.f90") +``` + +We specified that we have a Fortran project and told CMake to create an executable +``my_prog`` from the files ``tabulate.f90`` and ``functions.f90``. +CMake knows the details how to build the executable from the specified sources, +so we do not have to worry about the actual steps in the build process. + +{% capture note %} + +CMake's offical reference can be found at the +CMake webpage. +It is organised in manpages, which are also available with your local CMake +installation as well using ``man cmake``. While it covers all functionality of +CMake, it sometimes covers them only very briefly. +{% endcapture %} +{% include note.html title="Note" content=note %} diff --git a/learn/building_programs/compiling_source.md b/learn/building_programs/compiling_source.md index 710e890bc..65f267e97 100644 --- a/learn/building_programs/compiling_source.md +++ b/learn/building_programs/compiling_source.md @@ -5,7 +5,7 @@ permalink: /learn/building_programs/compiling_source --- The first step in the build process is to compile the source code. The -output from this step is generally known as the object code - a set of +output from this step is generally known as the object code — a set of instructions for the computer generated from the human-readable source code. Different compilers will produce different object codes from the same source code and the naming conventions are different. @@ -54,7 +54,7 @@ Windows. Some remarks: * The compiler may complain about the contents of the source file, if it -finds something wrong with it - a typo for instance or an unknown +finds something wrong with it — a typo for instance or an unknown keyword. In that case the compilation process is broken off and you will not get an object file or an executable program. For instance, if the word "program" was inadvertently typed as "prgoram": @@ -78,7 +78,7 @@ Using this compilation report you can correct the source code and try again. * The step without "-c" can only succeed if the source file contains a -main program - characterised by the `program` statement in Fortran. +main program — characterised by the `program` statement in Fortran. Otherwise the link step will complain about a missing "symbol", something along these lines: @@ -98,9 +98,9 @@ compiler running in a Cygwin environment on Windows. Compilers also differ in the options they support, but in general: -* Options for optimising the code - resulting in faster programs or +* Options for optimising the code — resulting in faster programs or smaller memory footprints; -* Options for checking the source code - checks that a variable is not +* Options for checking the source code — checks that a variable is not used before it has been given a value, for instance or checks if some extension to the language is used; * Options for the location of include or module files, see below; diff --git a/learn/building_programs/distributing.md b/learn/building_programs/distributing.md index 254bf751c..960915bbf 100644 --- a/learn/building_programs/distributing.md +++ b/learn/building_programs/distributing.md @@ -13,7 +13,7 @@ choose from: __Option 1: Distribute the entire source code__ -By far the simplest - for you as a programmer - is this one: you leave it +By far the simplest — for you as a programmer — is this one: you leave it up to the user to build it on their own machine. Unfortunately, that means you will have to have a user-friendly build system in place and the user will have to have access to suitable compilers. For build systems: @@ -79,17 +79,17 @@ end module user_functions * Provide a basic build script with a command like: ```shell -gfortran -o function.dll function.f90 -shared +gfortran -o functions.dll functions.f90 -shared ``` or: ```shell -ifort -exe:function.dll function.f90 -dll +ifort -exe:functions.dll functions.f90 -dll ``` -As said, you cannot control that the user has done the right thing - any -DLL "function.dll" with a function `f` would be accepted, but not necessarily +As said, you cannot control that the user has done the right thing — any +DLL "functions.dll" with a function `f` would be accepted, but not necessarily lead to a successful run. An alternative set-up would be to change the main program into a subroutine diff --git a/learn/building_programs/include_files.md b/learn/building_programs/include_files.md index 5557e11d6..79d272a4b 100644 --- a/learn/building_programs/include_files.md +++ b/learn/building_programs/include_files.md @@ -34,14 +34,14 @@ tabulate/ main/ tabulate.f90 sub/ - function.f90 + functions.f90 ``` -Compiling the file "function.f90" with the commands +Compiling the file "functions.f90" with the commands ```shell $ cd sub -$ gfortran -c function.f90 +$ gfortran -c functions.f90 ``` leads to this structure: @@ -51,18 +51,18 @@ tabulate/ main/ tabulate.f90 sub/ - function.f90 - function.mod - function.o + functions.f90 + user_functions.mod + functions.o ``` To successfully compile and subsequently build the program we need to -tell the compiler where it can find the file "function.mod": +tell the compiler where it can find the file "user\_functions.mod": ```shell $ cd main $ gfortran -c tabulate.f90 -I ../sub -$ gfortran -o tabulate tabulate.o ../sub/function.o +$ gfortran -o tabulate tabulate.o ../sub/functions.o ``` The result: @@ -74,9 +74,9 @@ tabulate/ tabulate.o tabulate (or tabulate.exe on Windows) sub/ - function.f90 - function.mod - function.o + functions.f90 + functions.o + user_functions.mod ``` Notes: diff --git a/learn/building_programs/index.md b/learn/building_programs/index.md index 12057538d..3271c9a48 100644 --- a/learn/building_programs/index.md +++ b/learn/building_programs/index.md @@ -8,7 +8,7 @@ author: Arjen Markus, Ondřej Čertík, Milan Curcic, Laurence Kedward, Brad Ric Languages like Fortran, C, C++ and Java, to name but a few, share certain characteristics: you write code in your language of choice but then you have to build an executable program from that source code. -Other languages are interpreted - the source code is analysed by a +Other languages are interpreted — the source code is analysed by a special program and taken as direct instructions. Two very simple examples of that type of language: Windows batch files and Linux shell scripts. @@ -38,7 +38,7 @@ it is simple to express in source code, a lot of things actually happen when the executable that is built from this code runs: * A process is started on the computer in such a way that it can write -to the console - the window (DOS-box, xterm, ...) at which you type the +to the console — the window (DOS-box, xterm, ...) at which you type the program's name. * It writes the text "Hello!" to the console. To do so it must properly interact with the console. diff --git a/learn/building_programs/linking_pieces.md b/learn/building_programs/linking_pieces.md index a128443d5..6c34b511c 100644 --- a/learn/building_programs/linking_pieces.md +++ b/learn/building_programs/linking_pieces.md @@ -29,10 +29,10 @@ program tabulate end program tabulate ``` -Note the `use` statement - this will be where we define the function `f`. +Note the `use` statement — this will be where we define the function `f`. We want to make the program general, so keep the -specific source code - the implementation of the function `f` - +specific source code — the implementation of the function `f` — separated from the general source code. There are several ways to achieve this, but one is to put it in a different source file. We can give the general program to a user and they provide a specific source code. @@ -60,12 +60,12 @@ program. Because the program "tabulate" depends on the module first. A sequence of commands to do this is: ```shell -$ gfortran -c function.f90 -$ gfortran tabulate.f90 function.o +$ gfortran -c functions.f90 +$ gfortran tabulate.f90 functions.o ``` The first step compiles the module, resulting in an object file -"function.o" and a module intermediate file, "function.mod". This module +"functions.o" and a module intermediate file, "user\_functions.mod". This module file contains all the information the compiler needs to determine that the function `f` is defined in this module and what its interface is. This information is important: it enables the compiler to check that you call @@ -77,13 +77,13 @@ check anything. The second step invokes the compiler in such a way that: * it compiles the file "tabulate.f90" (using the module file); -* it invokes the linker to combine the object files tabulate.o and function.o into an -executable program - with the default name "a.out" or "a.exe" (if you +* it invokes the linker to combine the object files tabulate.o and functions.o into an +executable program — with the default name "a.out" or "a.exe" (if you want a different name, use the option "-o"). What you do not see in general is that the linker also adds a number of extra files in this link step, the run-time libraries. These run-time -libraries contain all the "standard" stuff - low-level routines that do +libraries contain all the "standard" stuff — low-level routines that do the input and output to screen, the `sin` function and much more. If you want to see the gory details, add the option "-v". This instructs diff --git a/learn/building_programs/managing_libraries.md b/learn/building_programs/managing_libraries.md index 440c6c775..0c9eef5e9 100644 --- a/learn/building_programs/managing_libraries.md +++ b/learn/building_programs/managing_libraries.md @@ -14,7 +14,7 @@ Libraries contain any number of object files in a compact form, so that the command-line becomes far shorter: ```shell -$ gfortran -o tabulate tabulate.f90 function.o supportlib.a +$ gfortran -o tabulate tabulate.f90 functions.o supportlib.a ``` where "supportlib.a" is a collection of one, two or many object files, @@ -104,8 +104,8 @@ a dynamic link library) There is one more thing to be aware of: On Windows you must explicitly specify that a procedure is to be _exported_, i.e. is visible -in the dynamic library. There are several ways - depending on the -compiler you use - to achieve this. One method is via a so-called +in the dynamic library. There are several ways — depending on the +compiler you use — to achieve this. One method is via a so-called compiler directive: ```fortran @@ -129,11 +129,11 @@ we look at the `tabulate` program in the file "tabulate.f90". ## GNU/Linux and gfortran The `tabulate` program requires a user-defined routine `f`. If we -let it reside in a dynamic library, say "function.dll", we can simply +let it reside in a dynamic library, say "functions.dll", we can simply replace the implementation of the function by putting another dynamic library in the directory. No need to rebuild the program as such. -On Cygwin it is not necessary to explicitly export a procedure - all +On Cygwin it is not necessary to explicitly export a procedure — all publically visible routines are exported when you build a dynamic library. Also, no import library is generated. @@ -141,14 +141,14 @@ Since our dynamic library can be built from a single source file, we can take a shortcut: ```shell -$ gfortran -shared -o function.dll function.f90 +$ gfortran -shared -o functions.dll functions.f90 ``` -This produces the files "function.dll" and "function.mod". The +This produces the files "functions.dll" and "user\_functions.mod". The utility `nm` tells us the exact name of the function `f`: ```shell -$ nm function.dll +$ nm functions.dll ... 000000054f9d7000 B __dynamically_loaded U __end__ @@ -165,21 +165,21 @@ other routine "f" that might be defined in another module. The next step is to build the program: ```shell -$ gfortran -o tabulate tabulate.f90 function.dll +$ gfortran -o tabulate tabulate.f90 functions.dll ``` The DLL and the .mod file are used to build the executable program with checks on the function's interface, the right name and the reference -to "a" DLL, called "function.dll". +to "a" DLL, called "functions.dll". -You can replace the shared library "function.dll" by another one, implementing +You can replace the shared library "functions.dll" by another one, implementing a different function "f". Of course, you need to be careful to use the correct interface for this function. The compiler/linker are not invoked anymore, so they can do no checking. ## Windows and Intel Fortran The setup is the same as with Linux, but on Windows it is necessary -to explicitly export the routines. And an import library is generated - +to explicitly export the routines. And an import library is generated — this is the library that should be used in the link step. The source file must contain the compiler directive, otherwise the function `f` @@ -193,27 +193,27 @@ real function f( x ) Again we take a shortcut: ```shell -$ ifort -exe:function.dll function.f90 -dll +$ ifort -exe:functions.dll functions.f90 -dll ``` -This produces the files "function.dll", "function.mod" as well as "function.lib" (and two +This produces the files "functions.dll", "user\_functions.mod" as well as "functions.lib" (and two other files of no importance here). The "dependency walker" program tells us that the exact name of the function "f" is `FUNCTION_mp_F`. It is also exported, so that it can be found by the linker in the next step: ``` -$ ifort tabulate.f90 function.lib +$ ifort tabulate.f90 functions.lib ``` Note that we need to specify the name of the export library, not the DLL! (Note also: the Intel Fortran compiler uses the name of the first source file as the -name for the executable - here we do without the `-exe` option.) +name for the executable — here we do without the `-exe` option.) Just as under Cygwin, the DLL and the .mod file are used to build the executable program with checks on the function's interface, the right name and the reference -to "a" DLL, called "function.dll". +to "a" DLL, called "functions.dll". -You can replace the shared library "function.dll" by another one, but the same +You can replace the shared library "functions.dll" by another one, but the same caution is required: while the implementation can be quite different, the function's interface must be the same. diff --git a/learn/building_programs/runtime_libraries.md b/learn/building_programs/runtime_libraries.md index 196317c9c..5ba79ccbb 100644 --- a/learn/building_programs/runtime_libraries.md +++ b/learn/building_programs/runtime_libraries.md @@ -21,7 +21,7 @@ $ ldd tabulate.exe Other compilers or other versions of the same compiler will probably require different dynamic libraries. As long as you run the program on -the same computer - or, more accurately, within the same environment - +the same computer — or, more accurately, within the same environment — there should be no problem. However, when such a library cannot be found, you will get (hopefully) an error message and the program stops immediately. @@ -39,7 +39,7 @@ _On Linux:_ * The environment variable `LD_LIBRARY_PATH` is used. It consists of a list of directories to be searched, each directory separated via colons -(:) from the others. For instance: `/usr/lib:/usr/local/lib` - typical +(:) from the others. For instance: `/usr/lib:/usr/local/lib` — typical system directories. * At the link step you can also use an option to set `RPATH`, a list of directories that is put into the executable file itself. @@ -54,7 +54,7 @@ to be searched, but now the separating character is the semicolon (;). * A set of system directories is searched. Unfortunately, the details can change from one version of the operating -system to the next. The above is merely an indication - use tools like +system to the next. The above is merely an indication — use tools like "ldd" or "dependency walker" to find out what libraries are loaded and where they are found.