Instead we have a hand coded project that includes a .prj with a bunch of #implib statements that creates all of the libs from all of the EXPs. That way we can do a single pass compile.
– c/o @d.harms
Neat idea Dave, thanks!
Excerpt From “Basic Compiling and Linking”
The #implib command is used to create (if necessary) a dynamic link library file. There are two forms of this command, which operate in slightly different ways. If a single filename is specified, this names an import library file, which is created (if not up-to-date) from the object files in the link list. The object files are scanned and each public procedure or variable is exported. For example:
In the second form of the #implib command, an import library filename and a module definition file (.exp—see Module Definition File below) are both specified, and the library file is created (if not up-to-date) from the symbols named in the definition file. This form of the command is equivalent to using the tsimplib utility that comes with SoftVelocity C, C++, and Modula-2.
#implib <expfilename> <libfilename>
Using #implib in the second form requires you to create and maintain the list of exports ‘by hand’, whereas the first form exports all public names automatically. The use of a module definition file is an advantage if you need to maintain compatibility with previous versions of an interface, and it also allows you to export only the procedures which need to be exported.
When #implib is used with a module definition file, the link list is cleared.
On a somewhat related subject.
Here is a batch file (.cmd is the same as .bat), That I use to call MsBuild.
It’s part of the Clarion package for use in Sublime Text, and VS Code
It’s designed to take arguments, because I call it via the Sublime Text build project system
A given project is configured for what to compile, the configuration (build or release) and version of clarion - given by a folder
Note in ST3, packages are distributed in a zipped form
so you’ll need to extract the .cmd from inside of the package to be able to call it.
// What to build, convention is the same folder and basename as this sublime-project file, but with a .sln instead
// Build Configuration typcially Debug|Release
//(Optional) CW_Ver, such as "8.0.9579" will default to current
//uses perl style regex: (maybe this) http://www.cs.tut.fi/~jkorpela/perl/regexp.html
"file_regex": "^ \\s*[2>]*(.*)\\(([0-9]*),([0-9]*)"
// hard code in the 2 spaces at the start of the line, to JUST use the final summary error lines
// CON: this means I can't click on rows as the compile occurs
// PRO: In line error messages are no longer duplicated
//"shell" : true,
I found Dave Harms had written the above a CMag article on the topic of using #ImpLib with nice screen captures. Be aware the article example uses the wrong syntax that flips the file names as LIB EXP. In his post here he shows it right as #ImpLib DllA.EXP DllA.LIB
I used the Article to make an example using DLL Tutor that I attached.
Make a Win32 Project with a CLW that has nothing but MAP. CODE
Make a LibCreate.PR file like below and add it to the SLN under Projects to Include
---- LibCreate.PR ---- #implib ALLFILES.EXP ALLFILES.LIB #implib REPORTS.EXP REPORTS.LIB #implib UPDATES.EXP UPDATES.LIB
I don’t use this, I do a 3 pass MSBuild of DLLs, DLLs with Mutual Imports, EXEs. I’m not entirely sure how ImpLib can work with source control and multiple developers. It seems like a “chicken and egg problem.” The EXP file needs to be in Source control for use by the build server to perform #ImpLib. That file is generated and I’m not sure how stable it is so will it merge well?
Dave’s article had the correct syntax of #implib LIB EXP, it was wrong as #implib EXP LIB in the excerpt from page 130 of AdvancedTopicsReferenceGuide.pdf, the example projects in that PDF do show correctly e.g. #implib %%drvname%%.LIB %%drvname%%.EXP. The wrong syntax will trash your EXP and not give you a LIB.
I attached a corrected DLL Tutor example project to generate LIBs with the bwlow syntax. There’s not need to do AllFiles since if it is the first one built. #implib ALLFILES.LIB ALLFILES.EXP #implib REPORTS.LIB REPORTS.EXP #implib UPDATES.LIB UPDATES.EXP
I also changed its target to a LIB so there’s never a pointless EXE, and then in the Post Build Cmd is DEL LibCreate.lib
My tests show this is optimized to only make the new LIB if it changes, i.e. if you run it twice in a row the LIB is not created again. I would guess it makes it, checks its the same, then does not replace it, but I did not test this. The project language does allow “#if reports.lib #older reports.exp #then#implib …”
I realized this is not a check-n-egg problem and will work perfectly. Unlike interactive building using the IDE when you build in a Batch mode:
First you Generate CLWs for every APP and that makes the EXP files for every DLL
Next you run MSBuild on all the CwProj’s to compile the CLWs that were generated
The first CwProj simply needs to be the Lib Creator. I like this idea and will try it next week so I can take out step 2 of the MSBUILD that re-compiles the DLLs with mutual imports.
The one possible issue is since Clarion 7 you can skip the EXP file and specify EXPORT on a procedure or data. This is similar to C++ declspec(dllexport). The Linker will add it to the LIB file and not the EXP so #implib would make a bad file. I almost never do this, and only for quick and dirty tests or prototypes. I tried it but decided I liked the EXP because I use it for other things.
You can use LibMaker to go get the mangled name, then edit the EXP to add it. You must remove EXPORT from the procedure or it will be a duplicate. (Or easier, prototype in the importing project and the linker errors will have the mangled names it wants.)
FEATURE: New attribute for procedure prototypes and static variables declarations: EXPORT. The EXPORT attribute forces the variable or procedure to be added to the export list of the DLL even if it is not listed in the EXPORTS section of the EXP file.
There is also this feature. I forgot the reasons, I guess its like Name(‘xxx’). It can be found in other build systems like Visual Studio.
FEATURE: Ability to use substitution of exported name in the EXPORTS section of the EXP file. The entry in the EXPORTS section can have format [=] @?
The below DOS command will create the #implib lines in file ImpLib.TXT to paste into the .Pr file. This assumes the EXP files are present. It checks IF Exist .EXE and does not generate those.
DEL ImpLib.TXT & FOR %f IN (*.exp) DO IF not EXIST %~nf.EXE ECHO #implib %~nf.LIB %f >> ImpLib.TXT
In a BAT file you need % doubled to %%
ECHO This will make a ImpLib.TXT file with all the lines for the PR
FOR %%f IN (*.exp) DO IF not EXIST %%~nf.EXE ECHO #implib %%~nf.LIB %%f >> ImpLib.TXT
In one small project (8 DLL / 5 EXE) the second pass to compile the 4 DLL APPs with mutual imports only took 9 seconds. So skipping pass 2 by changing to use #implib will not see time any significant time savings. It will eliminate import errors on the first pass, and the build order will no longer matter (but I would try to do them in order). its the way to go.
This is very nice @CarlBarnes. Just used this technique on a project with some circular references where I was building into a new project folder that had never been built in.
I added the .PR with the implib statements to my data DLL since it always gets compiled first, rather than a separate project.
Thank you for the reminder on this! I have a C5 project where a Single DLL compiles to export 24 Classes. I need to split that into 24 DLLs of one class each. I first wrote a program to split the 3000 line EXP into 24 files. Them I used this to make 24 LIBs.
I’ll list the steps I took to make it easier to do this next time:
Create the ImpLib2Txt.BAT file in post #10 in the Project folder
Run ImpLib2Txt.BAT to create ImpLib.TXT with #ImpLib lines
Download ImpLibExampleForDllTutor_Corrected.zip attached in post 8
Unzip in your project folder
Edit LibCreate_ImpLib_prj.PR to add the #ImpLib lines from ImpLib.Txt in step 2
The reason I’m doing this is the Classes have Topspeed File declarations in the Class CLW. The C5 builds fine with those files are encapsulated as local. In C8 - C11.1 build throws duplicate symbol errors on the Keys. I’d say the compiler is making them public for the linker. So I am trying putting each Class in its own DLL since those KEY symbols are not DLL public.