How to build a DLL version of libjpeg 9b? - dll

I want to build a DLL version of libjpeg 9b. According to the document here, it seems that we need to add a preprocessor __declspec(dllexport) or __declspec(dllimport) before the declaration of each function to be exported, in addition to setting the Configuration Type to "Dynamic Library (.dll)". But this is not an easy job because there are so many functions in libjpeg. So, is there any short-cut or work-around to build a DLL libjpeg without or with little modification of the jpeglib.h? Is there any DLL-ready source of libjpeg 9b available? I am using Visual Studio 2015 on Windows 7 64bit. Thanks for your answer.
PS: I downloaded the source of libjpeg 9b from http://www.ijg.org/files/. Is this the official place to download it? I ask because the beginning bytes of .vcxproj (originally .v10) files seems invalid (C2 8B C2 AF C2 A8) so Visual Studio is unable to open it.

Published builds (static / dynamic) on [GitHub]: CristiFati/Prebuilt-Binaries - (master) Prebuilt-Binaries/LibJPEG.
Like almost all of the nowadays software, LibJPEG is also hosted on [GitHub]: winlibs/libjpeg - libjpeg-9b. I downloaded it from both places, did a comparison and only few minor differences (out of which none in the source code) popped up. I'm going to explain on the GitHub version. Unfortunately, there is no walkthrough on this, so I had to look inside some of the files in order to figure out what has to be done. Here's a list of steps:
1. Prepare the ground
1st thing is to extract the compressed (.zip / .tar.gz) file content into a folder (and have a Cmd console opened there). That folder contains a bunch of files. The 1st one that we need is makefile.vc. As it name suggests, it's a Makefile that consists of a series of rules/instructions used in general to build stuff.
As a remark, I'm using VStudio (Community) 2015 (14) for this task (but there should be no differences for newer versions).
The tool that deals with Makefiles is NMake ([MS.Docs]: NMAKE reference). We need to call it against that file. NMake is located in "${VSTUDIO_INSTALL_DIR}\VC\bin\nmake.exe" (the (Nix style) env var doesn't really exist, it's just a path placeholder); typically that is "%SystemDrive%\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\nmake.exe" (e.g. on my machine it's "C:\Install\x86\Microsoft\Visual Studio Community\2015\VC\bin\nmake.exe").
Note that when dealing with VStudio command line tools, it's better to either:
Use VCVarsAll (that's a (batch) tool that sets some env vars like %PATH%, %INCLUDE%, %LIB%, ...), so all the VStudio tools are available without their full path. But since it's also located in "%VSTUDIO_INSTALL_DIR%\VC\vcvarsall.bat" (check [SO]: Linking to CRT (unresolved external symbol WinMainCRTStartup) (#CristiFati's answer) for more details), it doesn't worth the effort to locate / call it
Use VS2015 Tools Command Prompt from Start Menu (which basically calls VCVarsAll)
[MS.Docs]: Building on the Command Line contains more details on this topic
Now that we know where NMake is located, let's run:
"${VSTUDIO_INSTALL_DIR}\VC\bin\nmake.exe" /f makefile.vc setup-v10
(don't forget to enclose NMake's path in DblQuotes("), especially if it contains SPACEs).
If running into issues, Google will probably yield solutions:
One very likely to be encountered: fatal error U1052: file 'win32.mak' not found - check [SO]: Visual Studio NMake build fails with: fatal error U1052: file 'win32.mak' not found (#CristiFati's answer)
setup-v10 is a Makefile target, which simply renames some of the files in the folder (frankly, I don't know why the files aren't like already renamed in the 1st place).
After running the command in the folder, there should be 2 VStudio solution files:
jpeg.sln - which contains one project:
jpeg.vcxproj - this is the project responsible of building the lib
apps.sln - which contains a bunch of projects (which I don't know/care what they do):
djpeg.vcxproj - this is the only one that I'm going to mention, since I'll be using it to test the built lib
2. Build the jpeg library
The 1st thing to notice is that the solution / project files generated in the previous section are for VStudio 2010. But that's not a problem, since VStudio 2015 is able to handle them (when loading them, it will do all the necessary conversions to bring them up to date).
Opening jpeg.sln, will reveal some (unpleasant) stuff about the solution (and project):
It only has Win32 (32bit or x86) platform
It only has Release configuration
As you already mentioned, it builds a static library
Anyway, the solution should build OOTB. After completion, you'd notice a Release folder which should contain (besides a bunch of intermediary - .obj files), a 4+ MB jpeg.lib file which is the static .lib. Knowing that the code (and project related files) is OK, let's move on to the next step.
2.1 Build the jpeg .dll
In order to avoid breaking existing functionality, we should create a new configuration (note that the process of creating a new platform for your project is (almost) the same):
From menu, choose Build -> Configuration Manager...
In the dialog box that popped, up click on the Release combo box and select < New...>
In the New Solution Configuration dialog box, select a name for the new configuration: I chose Release-DLL (and from now on I'm going to rely on this name)
Make sure to select Release in the Copy settings from combo-box
Check Create new project configurations
After pressing OK, the new the Release-DLL configuration will be identical to Release. Next step is to do the necessary changes to it, in order to achieve our goal. RClick on the jpeg project in the Solution Explorer (in the left side of VStudio window), and choose Properties:
In the image above, there's the jpeg project settings window, showing how they should look like at the end. I highlighted (using colors) different points of interest:
Blue: the Platform / Configuration pair:
That is being modified (in the topmost dialog box)
Active (in the VStudio main window in the back)
When modifying some Platform / Configuration settings make sure that it's the active one (the data in the blue rectangles is identical), otherwise you'd bang your head against the walls and waste time trying to figure out why something that seems correct, doesn't work as expected. On my opinion, when this dialog box pops up, it should have the active values, but that's not always the case.
Red: items that should be modified (or at least require attention):
Configuration Type should obviously be Dynamic Library (.dll)
Platform Toolset should be Visual Studio 2015 (I'm mentioning this, since it happened to open a VStudio 2010 project with VStudio2015, and it kept the old toolset, and continued to build with it, but that's only if you have both versions installed)
Green: items recommended to be modified. Those are only folders. Notice the $(Platform) variable (that I use as a good practice), which comes in handy when building for more than 1 platform (e.g. Win32 and x64)
Save All and build. After the build succeeds, a new folder Win32-Release-DLL will appear, and like the previous one, it will contain a bunch of files plus jpeg.dll. One might think that it's done, but it's not quite so. All the code is compiled/built in the .dll, but it's not exported, so the .dll is pretty much unusable. You can check many of .dll (or .exe) properties opening it with Dependency Walker (or newer [GitHub]: lucasg/Dependencies). You can look at the screenshots from [SO]: Excel VBA, Can't Find DLL Entry Point from a DLL file (#CristiFati's answer) - in our case the export area will be empty.
One final note: If you think that in the future you'll need to build for other platforms (x64, or even ARM), and also you'll need to do some debugging (add a Debug configuration), add the Debug configuration under Win32 platform first, and only then create the new platform from Win32, otherwise you'll need to add the Debug configuration for every platform created before adding the Debug configuration to Win32.
2.2 Export data from the .dll
Just as a note: besides the __declspec(dllexport) approach, there are 2 more (when dealing with exports from Win .dlls):
Pass the export list as arguments to the linker ([MS.Docs]: /EXPORT (Exports a Function))
Enumerate them in [MS.Docs]: Module-Definition Files
But, since we don't know the code and there might be many symbols to export, neither one of the 3 is scalable (they would probably require lots of research / work). Anyway, we'll stick to the original approach:
Save the following piece of code:
#pragma once
#if defined(_WIN32)
# if defined(LIBJPEG_STATIC)
# define LIBJPEG_EXPORT_API
# else
# if defined(LIBJPEG_EXPORTS)
# define LIBJPEG_EXPORT_API __declspec(dllexport)
# else
# define LIBJPEG_EXPORT_API __declspec(dllimport)
# endif
# endif
#else
# define LIBJPEG_EXPORT_API
#endif
in a file called jexport.h in the libjpeg source folder. This is a pretty standard header file that deals with .dll exports.
Next, add it to the project: in Solution Explorer, RClick on Header Files -> Add -> Existing Item...
Make use of the new file
--- jmorecfg.h.orig 2016-03-30 09:38:56.000000000 +0300
+++ jmorecfg.h 2017-06-09 21:04:33.762535400 +0300
## -30,6 +30,8 ##
* in all cases (see below).
*/
+#include "jexport.h"
+
#define BITS_IN_JSAMPLE 8 /* use 8, 9, 10, 11, or 12 */
## -245,7 +247,8 ##
/* a function referenced thru EXTERNs: */
#define GLOBAL(type) type
/* a reference to a GLOBAL function: */
-#define EXTERN(type) extern type
+
+#define EXTERN(type) extern LIBJPEG_EXPORT_API type
/* This macro is used to declare a "method", that is, a function pointer.
The above is a diff. See [SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (#CristiFati's answer) (Patching utrunner section) for how to apply patches on Win (basically, every line that starts with one "+" sign goes in, and every line that starts with one "-" sign goes out). But, since the changes are trivial, they can also be done manually. The file that needs to be changed, is jmorecfg.h (2 things are required):
Include the new file at the beginning (#include "jexport.h")
Modify line 251 (replace #define EXTERN(type) extern type by #define EXTERN(type) extern LIBJPEG_EXPORT_API type)
I consider this step some kind of a workaround (gainarie), but as I stated, the "real thing" would simply require too much work (and time).
Tell the compiler to take our changes into account
Edit the project settings (again be careful with Platform / Configuration), select Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions and replace the old value (in my case: WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS) to WIN32;LIBJPEG_EXPORTS;_CRT_SECURE_NO_WARNINGS;NDEBUG. What I did was:
Adding LIBJPEG_EXPORTS
Removing _LIB
Some shifting
[MS.Docs]: /D (Preprocessor Definitions) might contain some useful info.
As a side note, after the above changes, the original (or "normal") behavior (Release configuration) will not be OK (it will build with a bunch of warnings, but that's not correct technically). That is because I didn't want to rely on the _LIB macro to differentiate between the static / dynamic library build. To straighten things out, add LIBJPEG_STATIC in the Preprocessor Definitions for Release configuration (following the same procedure as above).
Save All and build. After the build succeeds, the jpeg.dll is overwritten, and what's more important: a new file jpeg.lib will be created; that tells us that jpeg.dll exports something. You can check it again with Dependency Walker.
3. Test the .dll
This is optional, I did it to be sure that what I did so far, was not in vain
By tests, I only refer to building and running, meaning that I don't do any functional testing (the functions exported from the .dll actually do whatever they're supposed to)
Load the apps.sln solution, preferably in a new VStudio instance. As I said, I only care about the djpeg project.
1st thing to do, is to build it for the existing Release configuration. But, because of the changes done to jpeg library, it won't build OOTB (there will be linker errors). In order to get rid of them, edit its settings, the Preprocessor Definitions (just like in the previous step), and add LIBJPEG_STATIC (don't forget the separator (;)).
The build should be successful and under djpeg\Release folder, there should be a djpeg.exe file. Attempting to run it, will succeed (and this is the only thing I care about).
Build a new executable that uses our .dll:
Just like in jpeg library's case, create a new configuration: Release-DLL (make sure to do all the steps, but don't change it)
There's only one change required: let the linker know where we built our .lib file. Go to project settings, Configuration Properties -> Linker -> Input -> Additional Dependencies: the 1st is Release\jpeg.lib. Obviously, the path is not correct, so we need to replace Release by Win32-Release-DLL (of course you could use VStudio macros (for Platform / Configuration)). Might check [MS.Docs]: .Lib Files as Linker Input.
Save All and build. After the build succeeds, under djpeg\Release-DLL, there should be a djpeg.exe file. Attempting to run it will fail, since it can't find jpeg.dll. Fix that by either copying jpeg.dll to (check [MS.Docs]: Dynamic-Link Library Search Order for the semantics):
The djpeg.exe's folder
One of the folders that's in the %PATH% env var (or the other way around, append the jpeg.dll folder to %PATH%)
Again, you can check the 2 executables with Dependency Walker (but the size difference says enough: the 2nd djpeg.exe is significantly smaller), to see which one depends on the jpeg.dll. Anyway, here's the output on my computer (yes, Dependency Walker can act as a CmdLine as well :) ):
e:\Work\Dev\StackOverflow\q044450813\src\libjpeg-libjpeg-9b>"c:\Install\x86\Depends\Dependency Walker-politist-texan\AllVers\depends.exe" /c /ot:static.txt djpeg\Release\djpeg.exe
e:\Work\Dev\StackOverflow\q044450813\src\libjpeg-libjpeg-9b>"c:\Install\x86\Depends\Dependency Walker-politist-texan\AllVers\depends.exe" /c /ot:dynamic.txt djpeg\Release-DLL\djpeg.exe
e:\Work\Dev\StackOverflow\q044450813\src\libjpeg-libjpeg-9b>type static.txt | findstr -i "jpeg.dll"
e:\Work\Dev\StackOverflow\q044450813\src\libjpeg-libjpeg-9b>type dynamic.txt | findstr -i "jpeg.dll"
[ ] e:\work\dev\stackoverflow\q044450813\src\libjpeg-libjpeg-9b\djpeg\release-dll\JPEG.DLL
[ ] e:\work\dev\stackoverflow\q044450813\src\libjpeg-libjpeg-9b\djpeg\release-dll\JPEG.DLL 2017-06-09 21:16 2017-06-09 21:16 237,056 A 0x00000000 0x0003ECC8 x86 GUI CV,Unknown 0x10000000 Unknown 0x0003E000 Not Loaded N/A N/A 0.0 14.0 6.0 6.0
Update #0
I've also:
Submitted the changes to [GitHub]: winlibs/libjpeg - Win: build libjpeg as a dll (rejected)
Tried to contact JPEG Reference (several times), but the form froze after hitting SEND (left it even overnight with no luck)

Related

Is there a way to export the predefined macros from a Keil build configuration?

Context:
I'm trying to automate some of the more mundane tasks in embedded development with Keil. The end result I'm aiming for is that clicking build in a Keil project will run a pre-build step that runs all the code through Uncrustify (a source code beautifier) to ensure it conforms to the company style-guide, and a post-build step which then runs the code through pc-lint (a static code analyser) to highlight any potentially unsafe code that it might find. I've written a PC utility that searches through the .uvproj file for the #define macros, the include paths and the file-paths all of which are needed for both tools and then modifies the pre and post-build user commands to call up my batch files which will manage both steps. The uncrustify part is working fine and the lint part is producing some sensible messages, but the signal-to-noise ratio isn't that great.
My problem:
Lint keeps on producing messages that seem to relate to macros that the Keil compiler is aware of, but that Lint isn't. I'm trying to find a way to plug that gap. I found a table of predefined macros documented on the Keil website, which seems like a good start, but rather than manually copying them into a static .lnt file, I'd like to find a way of grabbing the up-to-date values at the time the project gets built. This way, the "__ARMCC_VERSION" macro, for instance, would be updated whenever the developer updates his/her Keil compiler, rather than being stuck at a point in time whenever I manually copied it.
I'd love it if someone can answer my question directly, but I'd be equally pleased if someone has a viable suggestion for a more straightforward alternative approach I could try instead. Many thanks!
I am assuming you're using the Keil ARM Compiler.
From the Compiler User Guide:
To list macros that are defined on the command line, predefined by the compiler, and found in header and source files, use --list_macros with a non-empty source file.
To list only macros predefined by the compiler and specified on the command line, use --list_macros with an empty source file.
EDIT:
It looks like your SDK also adds a few macros.
From the µVision User's Guide:
The following control strings are added, depending on the use of MDK:
__UVISION_VERSION:
Major and minor version of µVision. For example: -D__UVISION_VERSION="520".
RTE:
Set when RTE is in use. For example: -D_RTE_.
__RTX:
Set when RTX Kernel has been selected in Options for Target - Target - Operation System. Not set when using RTE. For example: -D__RTX.
__MICORLIB:
Set when Use MicroLIB has been enabled in Options for Target - Target. For example: -D__MICROLIB.
__EVAL:
µVision runs in evaluation mode. License MDK-Lite. For example: -D__EVAL.
device header name:
Device header name.

CMake: Remove header dependency

Is there any way to make CMake "forget" about a file in the dependency tree? My original problem (to avoid the XY situation) is the following: I want to timestamp the build of a set of tools which have complicated dependencies among them and to other tools. Right now, I want to use a pure timestamp, but later I might want add some info from the repository (SVN). Whatever system I end up implementing needs to have the following characteristics (my "X"):
No unnecessary rebuilding: the executables should not be rebuilt on every make if the only change would be the timestamp.
Update on any change: if any tool is going to be rebuilt or relinked, either by changes to its code or to one of its dependencies, the timestamp needs to be updated.
My current solution goes along the lines of creating a custom command+target that invokes CMake at make time (so the command calls CMake itself with -P script.cmake) to generate a timestamp.h file. The main files of my tools would include that file, and the projects would depend on the target so that it gets rebuilt first.
However, this has its drawbacks: if I do update the timestamp file on every call to make, then CMake's dependency scanner would know about that file even if I do not list it as an explicit dependency of my tools. Thus, every make would trigger at least a recompilation of the respective "main" files and the corresponding relink. With tens of tools, this means slowing down the build when I may be working on just two or three of them at once.
So, I was thinking that my solution would be to somehow make CMake forget about that file when building its dependency tree for the "main" file of each tool. I would keep the dependency on the custom target that does depend on the file, so that it would be regenerated first on each call to make. However, the build tool would not consider that file as relevant to determine whether it is necessary to actually rebuild each individual tool. Thus, tools only with other changes would be rebuilt (satisfying my first criterion), and any change that causes a rebuild of a tool would obviously use the version just generated (fulfilling the second criterion).
To my chagrin, I have not found a way to make the dependency scanner forget about this file, so my solution cannot be put to use. How would I go about doing such a thing? Is it even possible, or is it completely the wrong way to go about this? I am using CMake 3.4, and my code is currently C++, but I would like a solution that did not rely on C/C++ specifics, since I have a different project (written in Fortran) in which I would also like to have build timestamping.
I've had almost the same problem than you are. Simply solved by pushing the timestamp header file into standalone target containing only this header generator command. After that you have several choices:
1.. Exclude that project from the build by the IDE you are using. For example, for the Visual Studio you can do it by several ways:
1.1. Project->Project Dependencies...->uncheck project with that header (not always works: Error while removing project dependency in VS2010)
1.2. Build->Configuration Manager...->uncheck project with that header
2.. Create an environment variable and use the condition with that variable around the add_dependencies command in the CMakeLists.txt file.
3.. Generate 2 standalone solutions through the cmake generator with included and with excluded add_dependencies in the CMakeLists.txt file.
I've used particulary [1.2]. When i need build and debug, then i uncheck the dependecy. By default, dependecy always checked, so there is no problem to miss timestamp build for a build server.
Note:
The timestamp header will be included in all projects you want to include that header (for example, through the add_library and add_executable) and you still can observe it in the IDE under a project item menu even if a project depends on the timestamp project indirectly. This is useful if you don't want to search for the timestamp project with the header to open it from there and want to open it from any project which has included that header.
So, in case of removing the timestamp header from the add_library or add_executable you won't have that opportunity.

Will code inside a preprocessor conditional enter the binary?

I have a Config.h file which includes various preprocessor conditionals based on the environment. The project has 2 targets. Via the preprocessor arguments, if the environment is the first target we may have BASE_URL = #"https://firsttarget.com/", whereas if the environment is the second target we may have BASE_URL = #"https://secondtarget.com/".
For legal purposes, if someone were to recompile the assembly, and go hunting to strings etc... we cannot have somebody finding the string #"https://firsttarget.com/" if they were looking into the binary for the second target app.
So my question is... are strings behind preprocessor conditionals removed during Archive if they are not relevant?
Sections in code between #ifdef and #endif (or #else) are not seen by the compiler unless the symbol given on the ifdef line is defined. You can easily show this by writing something that won't compile in such a section. And since the compiler doesn't see that code there is no way the value of that define can end up in the binary.
To see exactly what the compiler will work with you can select an implementation file in Xcode and chose "Product > Perform Action > Preprocess" from the menu. This will show you exactly what the compiler will work with. Of course there will also be the content from all the system .h files that are imported, so your code most likely is at the very end. Your #define lines will be stripped too, so to really check which strings will be used you need to chose a file that actually uses those macros.
And finally you can do the same thing an attacker might do - use the strings utility on the compiled binary and see what's in there. In Xcode build your app and select the "Show in Finder" option for your app bundle from the products group. There select "Show package contents" to open app your actual app bundle. The actual binary is in there with the same name as your bundle but no file extension. Then open a terminal window, type "strings " (that is the word strings followed by a space) and drag the binary on top of this. Then confirm with return. You will get a long list of every readable string from the binary (including every selector).

Can CMakeLists.txt depend on a file parsed by a function?

I am rather new to CMake, starting off for the first time with a larger project consisting of many subprojects.
For particular reasons (described below for the curious) I already have a set of include files that contain info about the source files needed for each CMake target (lib or exe) – and, for now, I prefer to (re)use these files (reason also described below)
Writing a function to parse these files and add their content as source files to the targets was a surprisingly easy task.
But – now the Problem:
Obviously I want to have each targets CMakeLists.txt depend on the particular include file, that generates the list of source files, so that changes on the include file will be detected as if it were changes to CMakeLists.txt itself, but I simply can’t find any references on how to accomplish that.
N.B.: I found AddFileDependencies but that is for adding dependencies on source files, not the CMakeLists.txt. However, CMake itself can figure out dependencies to included .cmake file somehow, so I figured, it should be possible to do somehow.
Background for the curious:
For this project (quite a number of libraries used by quite a number of executable targets, all organized as subprojects) I was using QMake (without actually using Qt itself) for setting up makefiles. Doing so I was able to use Qt Creator while still being able to generate Visual Studio Solution/Project files automagically. We’re also still in progress of evaluating different IDEs and the choice has not been made yet. But the most important reason to use a generator like QMake / CMake was not being forced to set up the VS files for all these subprojects manually.
Although I needed to trick QMake sometimes to do what I wanted to, things went quite well - even for the VS solution - except for one thing: Visual Studio messes up dependencies on Flex/Bison and other files using custom build rules. It keeps recompiling the Flex/Bison/other files saying „command line changed“ – which I gave up trying to fix.
For this reason I thougt, I’d try CMake as a generator instead, which looks very promising so far – although not having builtin precompiled header support in CMake is somewhat ridiculous these days (off topic, I know).
Since Qt Creators CMake support is by far not as good as the support for QMake projects, I firgured, using the approach of parsing the .pri files containing the source file list would enable me using QMake and CMake side by side – especially since the remaining project settings are rather less complicated than on most open source projects.
There's a nice trick which does exactly what you need. It's based on the idea I found in the git-revision module of #rpavlik see this so question
This is the overall idea:
Create a dummy timestamp file
Add a custom command which touches the timestamp whenever the input .pri file changes
include the timestamp file in your CMakeLists.txt
A possible implementation:
set(input_pri_file <path-to-the-input-pri-file>)
set(timestamp_file ${CMAKE_CURRENT_BINARY_DIR}/timestamp.cmake)
add_custom_command(
OUTPUT ${timestamp_file}
COMMAND ${CMAKE_COMMAND} -E touch ${timestamp_file}
MAIN_DEPENDENCY ${input_pri_file}
VERBATIM
COMMENT "Updating timestamp.cmake"
)
if(NOT EXISTS "${timestamp_file}")
file(WRITE ${timestamp_file} "") # create initial empty file
endif()
include(${timestamp_file})
# create the file list from input_pri_file
....
# use the file list
add_executable(main ${filelist})
Here's what happens when the .pri file changes:
the change triggers the execution of the custom command
which updates the timestamp
because the CMakeLists includes the timestamp it is dependent on it
so updating the timestamp triggers a re-configuration of the CMakeLists.txt
I use the configure_file() if I have some input that should retrigger CMake's configuration process. See e.g. How to make CMake reconfiguration depend on custom file? and configure_file()'s unit test
So in your case it would look something like:
configure_file(SomeInput.pri ${CMAKE_CURRENT_BINARY_DIR}/SomeInput.pri)
Then you use ${CMAKE_CURRENT_BINARY_DIR}/SomeInput.pri to generate the sources. Just make sure you do not add COPYONLY, because then configuration won't retrigger on changes of SomeInput.pri.
EDIT: Alternatively use - a relative new addition - the CMAKE_CONFIGURE_DEPENDS directory property.

How to reuse Fortran modules without copying source or creating libraries

I'm having trouble understanding if/how to share code among several Fortran projects without building libraries or duplicating source code.
I am using Eclipse/Photran with the Intel compiler (ifort) on a linux system, but I believe I'm having a bigger conceptual problem with modules than with the specific tools.
Here's a simple example: In ~/workspace/cow I have a source directory (src) containing cow.f90 (the PROGRAM) and two modules m_graze and m_moo in m_graze.f90 and m_moo.f90, respectively. This project builds and links properly to create the executable 'cow'. The executable and modules (m_graze.mod and m_moo.mod) are stored in ~/workspace/cow/Debug and object files are stored under ~/workspace/cow/Debug/src
Later, I create ~/workplace/sheep and have src/sheep.f90 as the program and src/m_baa.f90 as the module m_baa. I want to 'use m_graze, only: ruminate' in sheep.f90 to get access to the ruminate() subroutine. I could just copy m_graze.f90 but that could lead to code getting out of sync and doesn't take into account any dependencies m_graze might have. For these reasons, I'd rather leave m_graze in the cow project and compile and link sheep.f90 against it.
If I try to compile the sheep project, I'll get an error like:
error #7002: Error in opening the compiled module file. Check INCLUDE paths. [M_GRAZE]
Under Properties:Project References for sheep, I can select the cow project. Under Properties:Fortran Build:Settings:Intel Compiler:Preprocessor I can add ~/workspace/cow/Debug (location of the module files) to the list of include directories so the compiler now finds the cow modules and compiles sheep.f90. However the linker dies with something like:
Building target: sheep
Invoking: Intel(R) Fortran Linker
ifort -L/home/me/workspace/cow/Debug -o "sheep" ./src/sheep.o
./src/sheep.o: In function `sheep':
/home/me/workspace/sheep/src/sheep.f90:11: undefined reference to `m_graze_mp_ruminate_'
This would normally be solved by adding libraries and library paths to the linker settings except there are no appropriate libraries to link to (this is Fortran, not C.)
The cow project was perfectly capable of compiling and linking together cow.f90, m_graze.f90 and m_moo.f90 into an executable. Yet while the sheep project can compile sheep.f90 and m_baa.f90 and can find the module m_graze.mod, it can't seem to find the symbols for m_graze even though all the requisite information is present on the system for it to do so.
It would seem to be an easy matter of configuration to get the linker portion of ifort to find the missing pieces and put them together but I have no idea what magic words need to be entered where in the Photran UI to make this happen.
I confess an utter lack of interest and competence in C and the C build process and I'd rather avoid the diversion of creating libraries (.a or .so) unless that's the only way to make this work.
Ultimately, I'm looking for a pure Fortran solution to this problem so I can keep a single copy of the source code and don't have to manually maintain a pile of custom Makefiles.
So can this be done?
Apologies if this has already been documented somewhere; Google is only showing me simple build examples, how to create modules, and how to link with existing libraries. There don't seem to be (m)any examples of code reuse with modules that don't involve duplicating source code.
Edit
As respondents have pointed out, the .mod files are necessary but not sufficient; either object code (in the form of m_graze.o) or static or shared libraries must be specified during the linking phase. The .mod files describe the interface to the object code/library but both are necessary to build the final executable.
For an oversimplified toy problem such as this, that's sufficient to answer the question as posed.
In a larger project with more complex dependencies (in my case, 80+KLOC of F90 linking to the MKL version of LAPACK95), the IDE or toolchain may lack sufficient automatic or user-interface facilities to make sharing a single canonical set of source files a viable strategy. The choice seems to be between risking duplicate source files getting out of sync, giving up many of the benefits of an IDE (i.e. avoiding manual creation of make/CMake/SCons files), or, in all likelihood, both. While a revision control system and good code organization can help, it's clear that sharing a single canonical set of source files among projects is far from easy given the current state of Eclipse.
Some background which I suspect you already know: Typically (including ifort) compiling the source code for a Fortran module results in two outputs - a "mod" file that contains a description of the Fortran entities that the module defines that the compiler needs to find whenever it sees a USE statement for the module, and object code for the linker that implements the procedures and variable storage, etc., that the module defines.
Your first error (the one you solved) is because the compiler couldn't find the mod file.
The second error is because the linker hasn't been told about the object code that implements the stuff that was in the source file with the module. I'm not an Eclipse user by any means, but a brute force way of specifying that is just to add the object file (xxxxx/Debug/m_graze.o) as an additional linker option (Fortran Build > Settings, under Intel Fortran Linker > Command Line). (Other tool chains have explicit "additional object file" properties for their link stage - there may well be a better way of doing this for the Intel chain.)
For more involved examples you would typically create a library out of the shared code. That's not really C specific, the only Fortran aspect is that the libraries archive of object code needs to be provided alongside the mod files that the Fortran compiler generates.
Yes the object code must be provided. E.g., when you install libnetcdf-dev in Debian (apt-get install libnetcdf-dev), there is a /usr/include/netcdf.mod file that is included.
You can now use all netcdf routines in your Fortran code. E.g.,
program main
use netcdf
...
end
but you'll have link to the netcdf shared (or static) library, i.e.,
gfortran -I/usr/include/ main.f90 -lnetcdff
However, as user MSB mentioned the mod file can only be used by gfortran that comes with the distribution (apt-get install gfortran). If you want to use any other compiler (even a different version that you may have installed yourself) then you'll have to build netcdf yourself using that particular compiler.
So creating a library is not a bad solution.