How to make the target "private" in GNU make only for internal use? OR: how to best apply target values โ€‹โ€‹of variables?

I have some helper purposes in a makefile that I want to restrict for internal or "private" use (only) inside the makefile. That is, I want to specify these targets as dependencies from the makefile, but I want the target not to be specified as the build target from the command line. It is very similar to the private function from OOP: the goal is harmful (or simply does not make sense) to build separately.

I want a special target .HIDDEN or .PRIVATE or something similar, similar to what .PHONY does for non-file purposes, but I donโ€™t think it exists. The private keyword is for variables only.

What is a good / general / elegant way to protect a target for internal / private use only?

The best workaround I could come up with is to check $(MAKECMDGOALS) for "unacceptable" purposes, then an error if indicated; it seems inelegant. I am sure that the makefile can be rewritten to avoid this situation - perhaps an excellent solution, but it is not.


Under the neckline ... here's a contrived example to illustrate.

Although I'm looking for a general solution, one example of goals that are harmful as individual / primary goals is the inheritance of the target values โ€‹โ€‹of the variables:

 override CFLAGS += -Wall all : debug %.o : %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< debug : CFLAGS += -g3 -O0 release : CFLAGS += -O3 debug : CPPFLAGS += -DDEBUG release : CPPFLAGS += -DRELEASE debug release : foo.o bar.o main.o $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) clean: -rm -f *.o debug release .PHONY: all clean 

The implicit rule is duplicated (optional) for illustration. For the purpose of debug or release , foo.o and others inherit the corresponding CFLAGS and CPPFLAGS - if one make clean debug , all objects will be consistent. But, for example, if someone creates foo.o separately, he will not be able to inherit the corresponding flags; for example, make clean foo.o debug you get foo.o , built by default CFLAGS ; then when creating debug it does not need to be updated, so it will be associated with other objects with different optimizations or different macro settings. It will probably work in this case, but it is not what was intended. Marking foo.o etc., since illegal targets can interfere with this.


EDIT:

It is clear that my example (above) was not a good choice for my more general question: hiding goals was not the best way to fix the problem with my example. Here is a modified example illustrating the modified question, "How to apply target values?" - It is based on comments from @Michael, @Beta, @Ross below and allows you to pose and respond to this more limited scenario.

As described in previous answers below, in this case it is much better to create objects that have different assembly flags in different places. eg,

 bin_debug/%.o : %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< bin_release/%.o : %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c) DEBUG_OBJS = $(addprefix bin_debug/,$OBJS) RELEASE_OBJS = $(addprefix bin_release/,$OBJS) debug : $(DEBUG_OBJS) release : $(RELEASE_OBJS) debug release : $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) 

The template rule is duplicated because I think it should be (several "template goals" ( % ) to convince make all goals are built at once with one recipe, see the questions SO this and this ).

So add to the target flags:

 debug : CPPFLAGS += -DDEBUG release : CPPFLAGS += -DRELEASE 

But it still suffers:

 make bin_debug/foo.o 

will not get CPPFLAGS from debug . I accepted @Michael's answer below, as it made me think about this issue in a more useful way, but also answered some of my own rhetorical questions below.

+6
source share
5 answers

The problem you are trying to solve is legal, but you are heading for the worst possible way to solve it.

Declaring private goals does not make sense

When we write a Makefile, we describe the compilation work in terms of goals, sources and recipes. The progress of this work is described by a set of goals that have already been built. Now you precisely observe that the sequence

 make clean make foo.o make debug 

will create objects whose format is incompatible with foo.o , thus leaving your assembly directory in an inconsistent state. But it is very wrong to infer that the user should not explicitly construct foo.o Consider the following sequence:

 make clean # Wait for foo.o being compiles and # interrupt the build job with a signal make debug 

Since make sees that foo.o , he will return to his task where he would be left foo.o untouched when compiling subsequent blocks with different flags, leaving the same inconsistent state in the assembly directory as in the first scenario.

Therefore, if we could realize private goals in Make files, it would be inefficient and could convey a false sense of security, which in itself is even worse than uncertainty. Also, the solution you imagine destroys one of the most important advantages of using Makefile over shell scripts: Make makes it easy to continue the interrupted task where it was.

I have documented some other aspects of using Make files with respect to a set of objects already built into my answer to the question "What is the purpose of linking object files separately in a Makefile?" .

Another solution to your problem

To eliminate the problem of inconsistency of compilation flags, we can organize the saving of the constructed goals in a special directory, depending on the compilation flags used. Implementing this will fix the problem without forcing us to abandon the ease of resuming an interrupted compilation job.

Here is an implementation roadmap:

  • Define build profiles, here you have release and build .
  • Choose which compilation to use for each build profile.
  • Choose in which directory to store the constructed goals for each assembly profile.
  • Write your Makefile so that inline objects are saved in the directories you select. Please refer to Gnu make - how to get object files in a separate subdirectory .

Note. . In my opinion, the BSD make option has much better support for writing goals in a special directory, see my answer to the question "How to write a Makefile using different directories for goals and sources . " As a rule, I prefer the BSD make option because its documentation is short and consistent with the point, and it has many useful additional examples, since the assembly and assembly of the operating system in the BSD world is organized by this program.

+7
source

I do not think that there is any โ€œelegantโ€ way to have goals made somehow private. I think that the only solution that could be called elegant would be to rewrite your makefile so that it doesn't matter what target users invoke, as Beta suggests. It would also be useful to make your makefile more convenient and understandable.

A not-so-elegant, but fairly simple way to make goals "private" would be to rename the makefile to something other than one of the default names. Then put a new makefile in it that calls the "private" makefile to make it work. Sort of:

 .SUFFIXES: PUBLIC_TARGETS = all debug release clean REAL_MAKEFILE = private.mak define invoke_make $(1): $(REAL_MAKEFILE) $(MAKE) -f $(REAL_MAKEFILE) $(1) endef $(foreach target, $(PUBLIC_TARGETS), $(eval $(call invoke_make,$(target)))) .PHONY: $(PUBLIC_TARGETS) 

Obviously, this does not prevent a specific user from invoking the "private" goals, but hopefully this makes it clear that they should not do this. The fact that everything makes things private in object-oriented languages โ€‹โ€‹is all the same. It is always possible for a sufficiently specific user to bypass it.

+2
source

One solution to the problem is to transfer CPPFLAGS to the template rules (for example, bin_debug/%.o: CPPFLAGS... ) instead of the usual rule ( debug: CPPFLAGS... ), the end result:

 bin_debug/%.o : CPPFLAGS += -DDEBUG bin_debug/%.o : %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< bin_release/%.o : CPPFLAGS += -DRELEASE bin_release/%.o : %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c) DEBUG_OBJS = $(addprefix bin_debug/,$OBJS) RELEASE_OBJS = $(addprefix bin_release/,$OBJS) debug : $(DEBUG_OBJS) release : $(RELEASE_OBJS) debug release : $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) 

therefore make bin_debug/foo.o will get CPPFLAGS including -DDEBUG .

Now, let's say you have โ†’ 2 rules: debug, release, config01, config02, config03, ... each with its own CPPFLAGS .

One way might be to continue to duplicate all the template rules, but it is annoying if something changes. Also, it is really impossible to use in foreach . This seems convenient:

 debug : CPPFLAGS+=-DDEBUG release : CPPFLAGS+=-DRELEASE config01 : CPPFLAGS+=-DSOMETHING config02 : CPPFLAGS+=-DSOMETHINGELSE TARGETS = debug release config01 config02 OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c) define TARGET_template bin_$(1)/%.o : %.c $$(CC) $$(CFLAGS) $$(CPPFLAGS) -c -o $@ $< $(1): $(addprefix bin_$(1)/,$(OBJS)) # other TARGET-specific stuff here endef $(foreach tgt,$(TARGETS),$(eval $(call TARGET_template,$(tgt)))) 

But still the situation is not fixed make bin_debug/foo.o - CPPFLAGS still CPPFLAGS .

So, instead of creating the target value of a variable of type debug: CPPFLAGS+=... you may have a variable specific to the target, for example CPPFLAGS_debug , and then add each rule:

 CPPFLAGS_debug = -DDEBUG CPPFLAGS_release = -DRELEASE CPPFLAGS_config01 = -DSOMETHING CPPFLAGS_config02 = -DSOMETHINGELSE TARGETS = debug release config01 config02 OBJS = foo.o bar.o main.o # or $(SRCS:.o=.c) define TARGET_template bin_$(1)/%.o : CPPFLAGS+=$$(CPPFLAGS_$(1)) bin_$(1)/%.o : %.c $$(CC) $$(CFLAGS) $$(CPPFLAGS) -c -o $$@ $$< $(1): $(addprefix bin_$(1)/,$(OBJS)) # other TARGET-specific stuff here endef $(foreach tgt,$(TARGETS),$(eval $(call TARGET_template,$(tgt)))) 

Caution; higher may require more $$(...) s, unverified.

Problems? The best way?

+1
source

You can define private goals by running their name with two hyphens.

 --private-target: @echo private public-target: --private-target @echo public 

You can call make public-target , but make --private-target will complain about an unknown option:

 $ make public-target private public $ make --private-target /Library/Developer/CommandLineTools/usr/bin/make: unrecognized option `--private-target' 

 $ make --version GNU Make 3.81 Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This program built for i386-apple-darwin11.3.0 
+1
source

Thinking about it and trying the following:

 TEST := $(shell echo $$RANDOM) test : $(TEST) $(TEST): <tab>@echo tada $(TEST) 

then doing the command line make test on the command line seems to work, and I think it will be difficult to get the result without using the test target. Maybe this way can help?

0
source

Source: https://habr.com/ru/post/975895/


All Articles