I make and use static libs. My method. Reason: once they are stable, compile time goes down. If I use the same thing many times in all the libraries, it gets linked once, not for each use. Smaller programs. Any changes, only one place to go.

See here about how to make static libs.

I have over 15 libraries with many functions in each, and it is only the beginning. Each of the functions in these libraries have a relationship to each other, even if only in that they are at a similar low level/high frequency of use. I will have screenshots and copies of the files at the static lib page so you can see what I do. Warning: I program only in Linux. Another warning: I talk too much.

An attempt is made to make each library stand-alone, in that they do not require a previous library of mine.

But obviously, sooner or later, one builds upon another. Each will need its own declarations, initializations, data. Once you have sets of libraries that have different underlying library requirements, you start having problems. Declaring every library is a waste of time, and DOES take more compiler time to search and move through parts of libraries not used. Eventually, you will double declare, or double initialize, or fail to locate a needed resource, or not put things in the correct order or ...

Tracing and tracking the logic of the fault gets very time-consuming. Though time is free, it is the one thing we all want to save.

Good programming requires a fixed style. There are many different styles, some better than others. Your style is yours. On a large project, the most maddening thing is reviewing the history of the project by re-learning each different style of each programmer, or watching the evolution of a programmer's style through time. You never know what will change, next. Your expectations are repeatedly broken with each of the style changes.

Decent style - that allows someone else to view your work - suggests that we bring the new person into the complexity of the program in stages. Each file set ALWAYS has comments that explain the purpose, logic, and use of the following file. You will appreciate this 2 years later when you want to use or change those old libraries.

Modern style says we put major logical program flow in the main, and push the details into the functions. A lot of functions, we push them into separate libraries.
(Modern teachers say: main, call one function, go from there. That's just stupid. Get your good grade first, then - get real.)

A lot of libraries ... nightmare.

A standard organization:

define any new user type(UDT)
define function
initialize data structures and/or environment
return to main

Much of the organization is done for you by your IDE and/or make/build/compiler system. I didn't want to learn all about what is, sometimes, a new language, to write with the language of my choice.

I wrote my own build system. It does kinda make things a little 'fluffy' sometimes. But I NEVER have to worry about what should or should not be included, or who comes first.

I will talk specifically about FreeBASIC now.

In My World

Each library is split into several parts.

type - any new user types, and any global #defines. I have only one of these for all programs
dim - all dims, commons, globals, statics required - one per library
declare - all functions/subs exposed by this library - one per library
functions - the coded sub/functions - many times in separate files, but usually in same directory
init - any initialization required to use these functions - one per library
include - specific compiled library names - one per library


one include file with a #define that guards double inclusion of the library with a unique library identifier

Now, I have a separate include file that does a couple of things.

First, it has an include statement that will always include whatever is always needed. Copyright?, Defines, whatever.

Next are a sequence of tests for each of the library unique identifiers, as precompiler tests. #IFDEF, for example.
Every library ID is tested, and, if another library is required for THAT library, the new lib ID is now #DEFINEed.

Next, these tests are all run again, but now each test has an #INCLUDE for the associated library type file.
Next, these tests are all run again, but now each test has an #INCLUDE for the associated library dim file.
Next, these tests are all run again, but now each test has an #INCLUDE for the associated library declare file.
Next, these tests are all run again, but now each test has an #INCLUDE for the associated library .bas file with the function code.
Next, these tests are all run again, but now each test has a #INCLUDE for the associated library init file.
Next, these tests are all run again, but now each test has a #INCLUDE for the associated library include file.

To use, include the single include file that guards and defines the lib desired.
Include the master include file that has all the tests.

The result is that before any use, it will always be dimmed, declared, defined.
As a new library is created, its tests will be included into the list.
Write a program. Just add the new directory. We can scan the dir and read the files. Checkbox the dependencies. Or, even, scan the files for needed dependencies. Someday.

There is a little more involved. The include paths need to be named. Sometimes, there can be conflicts. Very, very rare.
I test for a specific define I place at the top of each file that allows me to redirect to a different directory with the same name libraries, but a little different functionality. This could be used, for example, to test for the OS in use. Most functions will have to be compiled twice: one for multiple threads, one for single thread. These will have to be in separate folders, selected at link time automatically by your master include file.