On 04/12/2021 16:23, pozz wrote:
> Il 04/12/2021 10:31, Stefan Reuther ha scritto:
>> I'm not sure what you need order-only dependencies for. For a project
>> like this, with GNU make I'd most likely just do something like
>>
>> OBJ = file1.o module1/file2.o module2/file3.o
>> main: $(OBJ)
>> $(CC) -o $@ $(OBJ)
>> $(OBJ): %.o: $(SRCDIR)/%.c
>> mkdir $(dir $@)
>> $(CC) $(CFLAGS) -c $< -o $@
>
I almost never use makefiles that have the object files (or source
files, or other files) specified explicitly.
CFILES := $(foreach dir,$(ALLSOURCEDIRS),$(wildcard $(dir)/*.c))
CXXFILES := $(foreach dir,$(ALLSOURCEDIRS),$(wildcard $(dir)/*.cpp))
OBJSsrc := $(CFILES:.c=.o) $(CXXFILES:.cpp=.o)
OBJS := $(addprefix $(OBJDIR), $(patsubst ../%,%,$(OBJSsrc)))
If there is a C or C++ file in the source tree, it is part of the
project. Combined with automatic dependency resolution (for which I use
gcc with -M* flags) this means that the make for a project adapts
automatically whenever you add new source or header files, or change the
ones that are there.
> This is suboptimal. Every time one object file is created (because it is
> not present or because prerequisites aren't satisfied), mkdir command is
> executed, even if $(dir $@) is already created.
Use existence-only dependencies:
target/%.o : %.c | target
$(CC) $(CFLAGS) -c $< -o $@
target :
mkdir -p target
When you have a dependency given after a |, gnu make will ensure that it
exists but does not care about its timestamp. So here it will check if
the target directory is there before creating target/%.o, and if not it
will make it. It probably doesn't matter much for directories, but it
can be useful in some cases to avoid extra work.
And use "mkdir -p" to make a directory including any other parts of the
path needed, and to avoid an error if the directory already exists.
>
> A better approach is to use a dedicated rule for directories, but it's
> very complex and tricky[1].
The reference you gave is okay too. Some aspects of advanced makefiles
/are/ complex and tricky, and can be hard to debug (look out for mixes
of spaces instead of tabs at the start of lines!) But once you've got
them in place, you can re-use them in other projects. And you can copy
examples like the reference you gave, rather than figuring it out yourself.
>
> I think your approach is better, only because is much more
> understandable, not because is more efficient.
>
My version is - IMHO - understandable /and/ efficient.
>
>>> Dependencies must be created as a side effect of compilation with
>>> esoteric -M options for gcc.
>>
>> It's not too bad with sufficiently current versions.
>>
>> CFLAGS += -MMD -MP
>> -include $(OBJ:.o=.d)
>
> Are you sure you don't need -MT too, to specify exactly the target rule?
>
The exact choice of -M flags depends on details of your setup. I prefer
to have the dependency creation done as a separate step from the
compilation - it's not strictly necessary, but I have found it neater.
However, I use two -MT flags per dependency file. One makes a rule for
the file.o dependency, the other is for the file.d dependency. That
way, make knows when it has to re-build the dependency file.
>
>>> Is cmake simpler to configure?
>>
>> CMake does one-configuration-per-invocation type builds like sketched
>> above, i.e. to build target1/Release and target1/Debug, you invoke CMake
>> on two different workspaces, once with -DCMAKE_BUILD_TYPE=Release and
>> once with -DCMAKE_BUILD_TYPE=Debug.
>
> Yes, I was asking if the configuration file of CMake is simpler to write
> compared to a Makefile.
>
I've only briefly looked at CMake. It always looked a bit limited to me
- sometimes I have a variety of extra programs or steps to run (like a
Python script to pre-process files and generate extra C or header files,
or extra post-processing steps). I also often need different compiler
flags for different parts of a project. Perhaps it would work for what
I need and I just haven't read enough.
>
> [1]
https://ismail.badawi.io/blog/automatic-directory-creation-in-make/