35

The Problem:

Is it possible to give a target a different name or alias, such that it can be invoked using either the original target name or the alias.

For example something like

/very/long/path/my_binary: dep_a dep_b dep_c
    # Compile

# Desired command
ALIAS my_binary = /very/long/path/my_binary

# NOTE: Notice the use of 'my_binary' in the dependencies
data1: my_binary datafile
    # Build data file using compiled my_binary

Attempt 1: .PHONY

I have tried using a .PHONY target:

.PHONY: my_binary
my_binary: /very/long/path/my_binary

This works great when invoked from the command-line:

# Runs rule 'my_binary' and then *only* runs rule '/very/long/path/my_binary'
# if the rule '/very/long/path/my_binary' needs updating.
make my_binary

However, this does not work well when the alias my_binary is listed as a dependency:

# *Always* thinks that rule 'data1' needs updating, because it always thinks that
# the .PHONY target 'my_binary' "needs updating". As a result, 'data1' is
# rebuilt every time.
make /very/long/path/my_binary

Possible hack?

A possible hack is to use an empty target as suggested in an answer to this question, but that would require introducing fake files with names corresponding to the alias:

my_binary: /very/long/path/my_binary
    touch my_binary

This will clutter the main directory with files! Placing the fake files in a sub-directory would defeat the purpose, as the alias would have to be referred to as 'directory/my_binary'

Community
  • 1
  • 1
WaelJ
  • 2,942
  • 4
  • 22
  • 28

3 Answers3

24

Okay, I needed something similar. The path to my output artifacts were quite long, but I wanted short target names and also benefit easily from bash-completion.

Here is what I'm came up with:

os := [arbitrary long path to an artifact]
platform := [arbitrary long path to a differ artifact]
packer := [common parts of my packer build command]

.PHONY: all
all: $(platform)

.PHONY: platform
platform: $(platform)

$(platform): platform.json  $(os)
    @$(packer) $<

.PHONY: os
os: $(os)

$(os): os.json
    @$(packer) $<

.PHONY: clean
clean:
    rm -fr build/

With the Makefile above you can say:

$ make os
$ make platform

Which will be aliases for the long artifact names. I've made the snippet above quite long, because it's important to see the relationships between the .PHONY aliases and the real targets. I hope that works for you.

Note: I did not delete the clean target from the above example, because many people does not make that a .PHONY target. However, semantically it should be.

frncmx
  • 336
  • 2
  • 6
  • 2
    Note that it's important to make dependencies depend on the real targets, not their aliases (i.e. `$(platform): platform.json $(os)` and not `$(platform): platform.json os`. – Vladimir Panteleev Aug 22 '19 at 21:49
  • 1
    Isn't this exactly the same as OP described in "Attempt 1: .PHONY"? – André Chalella Oct 03 '19 at 00:35
  • 1
    I believe it's not the same. In my attempt imagine the `.PHONY` target as an interface just for the end-user, i.e., only use the `.PHONY` target when you call `make ` from your shell. Use the 'real' target whenever you are describing your dependency tree in the makefile. => Comfortable for the end-user; a little bit tedious for the developer writing the make file. Result: correct dependency calculation; good UX. If you have something like `binary: my-phony-target` your binary will be rebuilt on every call as phony targets are always outdated by definition and transitivity applies. – frncmx Oct 08 '19 at 12:47
  • 2
    Certainly it's trivial to create "aliases" that can be used from the command line. That's extremely common. But the OP wanted to also use the alias from _inside_ the makefile (list it as a prerequisite). Here you're not doing that, you're using the full path as a prerequisite (via the variable). – MadScientist Apr 05 '20 at 14:19
  • @MadScientist I'm not sure I get what you mean. My solution only adds to your solution. The two differences are: 1. I use `:=` (simply expanded var) instead of `=` (recursive var) 2. I added a trick to have a command-line alias, too (not just a shorthand in the Makefile) Besides the above two, the variable can be used as a prerequisite the same way as in your solution, e.g., `$(platform): platform.json $(os)` where `os` was the variable. – frncmx Apr 06 '20 at 16:03
19

I don't think there's any way to do it so that you can use the alias from within your makefile as well as the command line, except by creating those temporary files.

Why can't you just set a variable in the makefile, like:

my_binary = /very/long/path/my_binary

then use $(my_binary) everywhere in the makefile? I don't see any point in creating a real alias target for use inside the makefile.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • Might be off-topic, but if you go with this option, how would you invoke it in a loop? As in `for p in $$($(my_binary) --some-flag); do ...`? I can't seem to make that work. – Javier García Manzano Nov 15 '21 at 11:41
  • What you wrote will work. If it doesn't work please create a new question showing exactly what you did, the command you typed, the output you got, and describe what's wrong with that output and what you wanted to get. Please cut and paste code, with proper formatting: don't use images and don't paraphrase commands and errors. – MadScientist Nov 15 '21 at 13:48
4

I had a somewhat similar need. I wanted users of my makefile to be able to enter any of the following to accomplish the same result, such that the following were effectively synonyms of each other:

make hit list
make hitlist
make hit_list

What I did in my makefile was the following:

hit_list:

        @echo Got here
        <the real recipe goes here>

hit: hit_list
hitlist: hit_list
.PHONY: list
list:
        @echo > /dev/null

Then, when I tested it using any of the commands "make hit list", "make hitlist", or "make hit_list", I got identical results, as intended.

By extension, if one of your targets was the one with the long name but you used this approach whereby a simple short name identified the target with the long name as a prerequisite, I think that you should be able to say "make short_name" and accomplish what you're asking about.

This differs from your Approach 1 in that none of the synonyms is defined as a phony target (considering that "make hit list" is a command to make two targets, the second being effectively a noop), so the complication that you described would not arise.

camille
  • 16,432
  • 18
  • 38
  • 60
fireblood
  • 151
  • 4