Welcome toTencent Cloud + community, get more Tencent mass technology practice dry goods oh ~

This article was published by Mr. Log Bridge in cloud + community column

introduce

If you have experience installing software from source code on a Linux server, you may encounter the make utility. This tool is mainly used to automatically compile and build programs. It allows the application author to easily lay out the steps needed to build that particular project.

Although make was created to automate software compilation, the tool is designed to be flexible enough to automate almost any task that can be done from the command line. In this tutorial, we’ll discuss how to readjust make to automate repetitive tasks that happen in sequence.

We’ll demonstrate this on Ubuntu, but it should work in a similar fashion on almost any Linux server.

Install the Make

Before we can start using make, we need to install it.

Although we can install it by name, it is usually installed with other tools that help you compile your software. We’re going to install all of these because they’re generally very useful. There is a package called “build-essential” that contains make and other programs:

sudo apt-get update
sudo apt-get install build-essential
Copy the code

You now have tools that allow you to leverage make in normal capacity.

Understand the Makefile

The main way to receive instructions with the make command is to use makefiles.

From the man page, we can see that make will find the file named GNUmakefile, then find the makefile, then find the makefile. It recommends that you use makefiles because GNU Makefiles are GNU specific commands, and makefiles don’t stand out.

Makefiles are directory-specific, which means make will search in the directory where it was called to find these files. Therefore, we should place the Makefile in the root of the task we are going to perform, or call the script we are going to write where it makes most sense.

In makefiles, we follow a specific format. Using the concepts of target, source, and command in this way:

target: source
	command
Copy the code

This alignment and formatting is important because make is picky. Here we discuss the format and meaning of each component:

target

Target is a user-specified name used to reference a set of commands. You can think of it like a simple function in a programming language.

Target, aligned on the left column, is a contiguous word (no Spaces) ending with a colon (:).

When calling make, we can specify target by typing:

make target_name
Copy the code

Make then checks the Makefile and executes the command associated with that target.

source

Source is a reference to a file or other target. They represent preparation or dependencies for the goals associated with them.

For example, you could make part of a make file look like this:

target1: target2
    target1_command

target2:
    target2_command
Copy the code

In this example, we can call target1 like this:

make target1
Copy the code

Make will then go to the Makefile and search for the “target1” target. It then checks to see if there is a specified source.

It finds the “target2” source dependency and temporarily jumps to that target.

From there, it will check if target2 lists any sources. It doesn’t, so it will continue to execute the “target2 command”. At this point, make reaches the end of the “target2” command list and passes control back to the “target1” target. It will then execute the “target1 command” and exit.

The source can be a file or the target itself. Use the file timestamp to see if the file has changed since the last call. Rerun the target if changes have been made to the source file. Otherwise, it marks the dependency as completed and continues to the next source, or command if this is the only source.

The general idea is that by adding sources, we can build a set of sequential dependencies that must be executed before the current target. You can specify multiple space-delimited sources after any target. You can begin to understand how to specify an elaborate sequence of tasks.

command

The reason for this flexibility with the make command is that the command part of the syntax is very open. You can specify any command to run under the target. You can add as many commands as you need.

The command is specified on the line following the target declaration. They are indented by a TAB character. Some versions of Make are flexible about how they indent parts of the command, but in general, you should stick to a single TAB to make sure make recognizes your intent.

Make treats each indentation under the target definition as a separate command. You can add as many indentation and commands as you need. Make will browse through them one at a time.

Before the command tells make to process them differently, we can place a few things:

  • – : The dash before the command tells make not to abort if it encounters an error. For example, this action can be useful if you want to execute a command on a file (if it exists), and nothing if it doesn’t.
  • @ : If you use the “@” symbol to boot a command, the command call itself is not printed to standard output. This is primarily used to clean up the resulting output.

Additional features

Some other features help you create more complex chains of rules in makefiles.

variable

Make identifies variables (or macros) that act as simple placeholders for replacements in makefiles. It is best to declare these at the top of the file. The name of each variable is fully capitalized. After the name, the equal sign assigns the name to the value on the right. For example, if we wanted to define the installation directory as /usr/bin, we could add at the top of the file:

INSTALLDIR=/usr/bin
Copy the code

Later in the file, we can refer to this location using the following syntax:

$(INSTALLDIR)
Copy the code

Span multiple lines

Another useful thing we can do is to allow commands to span multiple lines.

“\” to indicate crossing lines:

target: source
    command1 arg1 arg2 arg3 arg4 \
    arg5 arg6
Copy the code

This becomes even more important if you take advantage of some of the shell’s more programming features, such as if-then statements:

target: source
    if [ "condition_1"= ="condition_2"]; \then\
        commandto execute; \ anothercommand; \else\
        alternative command; \fi
Copy the code

This will execute the block as if it were a line of command. In fact, we could have written it as a single line, but it improves readability by splitting it up into something like this.

If you want to escape end-of-line characters, make sure there are no extra Spaces or tabs after the “\”, or you will receive an error.

File suffixes rule

Another feature you can use if you are doing file processing is file suffixes. These are general rules that provide a way to process files based on extensions.

For example, if you wanted to process all the.jpg files in your directory and convert them to.png files using the ImageMagick suite, we could use the following in a Makefile:

.SUFFIXES: .jpg .png

.jpg.png:
    @echo converting $< to $@
    convert $< $@
Copy the code

The first part is SUFFIXES. This tells make all the suffixes we will use in the file suffixes. The default includes suffixes commonly used to compile source code, such as “.c “and”.o “files, which do not need to be marked in this declaration.

The next section is the declaration of the actual suffix-rule. This basically takes the following form:

original_extension.target_extension:
Copy the code

This is not an actual target, but it will match any calls to files with the second extension and build them out of files with the first extension.

In our example, if we have “file.jpg” in our directory, we can call make to build a file named “file.png” :

make file.png
Copy the code

Make will see the PNG file in the.SUFFIXES declaration and look at the rules for creating the “. PNG “file. It then looks in the directory for the target file with “.png “replaced by”.jpg “. It will then execute the following commands.

The postfix rule uses some variables that we haven’t covered yet. These help replace different information depending on which part of the current process:

  • $? : This variable contains a list of dependencies newer than the current target. These will be the goals that must be re-completed before the command under this goal can be executed.
  • $@ : This variable is the name of the current target. This allows us to reference the file you are trying to make, even though this rule matches the pattern.
  • $< : This is the name of the current dependency. For the suffix rule, this is the name of the file used to create the target. In our example, this will contain “file.jpg”
  • $* : This file is the name of the current dependency that strips the matching extension. Think of this as an intermediate stage between the target and source files.

Create the conversion Makefile

We will create a Makefile that will perform some image processing and then upload the files to our file server so that our website can display them.

If you would like to follow along, download the ImageMagick conversion tool before you begin. These are simple command-line tools for manipulating images, and we’ll use them in our script:

sudo apt-get update
sudo apt-get install imagemagick
Copy the code

In the current directory, create a file called Makefile:

nano Makefile
Copy the code

In this document, we will begin implementing transformation goals.

Convert all JPG files to PNG

Our servers have been set up specifically to service.png images. Therefore, we need to convert any.jpg files to.png before uploading.

As mentioned above, postfix rules are a good way to do this. We’ll start with the.suffix statement, listing the format of the conversions between us:

.SUFFIXES: .jpg .png
Copy the code

After that, we can make a rule to change the.jpg file to.png. We can do this using the convert command in the ImageMagick suite. The convert command is simple and has the syntax:

convert from_file to_file
Copy the code

To implement this command, we need postfix rules to specify the format we start with and the format we end with:

.SUFFIXES: .jpg .png

.jpg.png:           ## This is the suffix rule declaration
Copy the code

Now that we have the matching rules, we need to implement the actual transformation steps.

Since we don’t know exactly what filename will be matched here, we need to use the variables we learned. Specifically, we need to reference $< as the original file and $@ as the file we want to convert. If we combine this with what we know about the convert command, we get the following rule:

.SUFFIXES: .jpg .png

.jpg.png:
    convert $< $@
Copy the code

Let’s add some functionality that tells us exactly what’s going on with the Echo statement. We will include the “@” symbol before the new command and the commands we already have to print the actual command at execution time:

.SUFFIXES: .jpg .png

.jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
Copy the code

At this point, we should save and close the file so that we can test it.

Get JPG files to the current directory. If you do not have the file at hand, you can enter the following content to obtain it from Tencent Cloud website:

wget https://ask.qcloudimg.com/raw/qctrain/yehe-b5f4bb2e421e9/1kp7y81up9.jpg
mv digitalocean-badge-blue.jpg badge.jpg
Copy the code

You can test that your make file works by asking it to create a badger.png file:

make badge.png
Copy the code
converting badge.jpg to badge.png using ImageMagick...
conversion to badge.png successful!
Copy the code

Make will go to the Makefile, see. SUFFIXES declaration in. PNG, then go to the matching SUFFIXES rule. Then run the listed commands.

Creating a file List

At this point, if we explicitly tell it we want the file, make can create a.png file.

It would be better if it just created a list of.jpg files in the current directory and converted them. We can do this by creating a variable that contains all the files to be converted.

The best way to do this is with the wildcard directive:

JPG_FILES=$(wildcard *.jpg)
Copy the code

We can specify a target with bash wildcards like this:

JPG_FILES=*.jpg
Copy the code

But there is a drawback. If there is no.jpg file, this will actually try to run the conversion command on a file named “*.jpg”, which will fail.

The wildcard syntax we mentioned above compiles a list of.jpg files in the current directory and does not set the variable to anything if it does not exist.

While we do this, we should try to handle slight changes to common.jpg files. These image files usually use the.jpeg extension instead of.jpg. To deal with these problems in a sensible way, we can change the name in the program to a.jpg file to make our processing lines simpler:

Instead, we’ll use the following two:

JPEG=$(wildcard *.jpg *.jpeg)     ## Has .jpeg and .jpg files
JPG=$(JPEG:.jpeg=.jpg)            ## Only has .jpg files
Copy the code

The first line compiles a list of.jpg and.jpeg files in the current directory and stores them in a variable called JPEG.

The second line references this variable and performs a simple name conversion, converting names ending in.jpeg in a JPEG variable to names ending in.jpg.

This is done with the following syntax:

$(VARNAME:.convert_from=.convert_to)
Copy the code

At the end of these two lines, we will have a new variable named JPG that will contain only the.jpg file name. Some of these files may not actually exist on the system because they are actually.JPEG files (no actual renaming has occurred). This doesn’t matter, because we’ll only use this list to create a new list of the.png files we want to create:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
Copy the code

Now, we have a list of files that we want to request in the variable PNG. This list contains only.png file names because we did another name conversion. Now, each.jpg or.jpeg file in this directory is used to compile the list of.png files we want to create.

We still need to update. SUFFIXES declaration and SUFFIXES rules to reflect the.jpeg files we are now working with:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
Copy the code

As you can see, we have added.jpeg to the suffix list and added another suffix match to our rule.

Create some Targets

We now have a lot in makefiles, but we don’t have any normal targets yet. Let’s fix this so that we can pass the PNG list to the suffix rule:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

convert: $(PNG)

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
Copy the code

All of these new targets list our collection of.png file names as requirements. Then see if there is a way to take a.png file and use the suffix rule to do so.

Now we can use this command to convert all of our.jpg and.jpeg files to.png files:

make convert
Copy the code

Let’s add another target. Another task that is usually done when uploading images to a server is resizing them. Having the image properly sized will eliminate the need for the user to dynamically resize the image upon request.

ImageMagick’s Mogrify command can resize the image the way we want it to. Assume that the area our image will display on our website is 500px wide. We can convert this area using the following command:

mogrify -resize 500\> file.png
Copy the code

This will adjust any image larger than 500px wide to fit this area, but will not touch smaller images. That’s what we want. As a target, we can add the following rules:

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!
Copy the code

We can add it to our file like this:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
Copy the code

Now we can string these two goals together as a dependency on another goal:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

webify: convert resize

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
Copy the code

You may notice that implicit resizing runs the same command as convert. We will specify both, though not always. Transformations can include more fine-grained processing in the future.

The Webify target can now convert images and resize them.

Upload the file to the remote server

Now that we have our images ready for the Web, we can create a target and upload them to a directory of still images on our server.

We can do this by passing the converted file list to SCP:

Our goal looks something like this:

upload: webify
    scp $(PNG) root@ip_address:/path/to/static/images
Copy the code

This will upload all our files to the remote server. Our file now looks like this:

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

upload: webify
    scp $(PNG) root@ip_address:/path/to/static/images

webify: convert resize

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful!
Copy the code

Clean up the

Let’s add a cleanup option to delete all local.png files after they have been uploaded to a remote server:

clean:
    rm *.png
Copy the code

Now we can add another target at the top that will be called after we upload the file to the remote server. That would be the most complete goal, and that’s what we want to default to.

To specify this, we will use it as the first available target. This will be used as the default. We will conventionally call it “all” :

JPEG=$(wildcard *.jpg *.jpeg)
JPG=$(JPEG:.jpeg=.jpg)
PNG=$(JPG:.jpg=.png)
.SUFFIXES: .jpg .jpeg .png

all: upload clean

upload: webify
    scp $(PNG) root@ip_address:/path/to/static/images

webify: convert resize

convert: $(PNG)

resize: $(PNG)
    @echo resizing file...
    @mogrify -resize 648\> $(PNG)
    @echo resizing is complete!

clean:
    rm *.png

.jpeg.png .jpg.png:
    @echo converting $< to $@ using ImageMagick...
    @convert $< $@
    @echo conversion to $@ successful
Copy the code

With these final actions, if you enter a directory using makefiles and.jpg or.jpeg files, you can call make without any arguments to process the files, send them to your server, and then delete the.png files you uploaded.

make
Copy the code

As you can see, it’s easy to chain tasks together, and you can select a process to a point. For example, if you just want to convert files and need to host them on a different server, you can use the Webify target.

conclusion


At this point, you should have a good understanding of how to use makefiles. More specifically, you should know how to use make as a tool to automate most processes.

While it may be easier to write a simple script in some cases, makefiles are an easy way to establish a structured hierarchical relationship between processes. Learning how to use this tool can help simplify repetitive tasks. For more Makefile tutorials, please visit Tencent Cloud + community to learn more.


How To Use Makefiles To Automate Tasks on an Ubuntu VPS

Question and answer

The difference between BeautifulSoup and Scrapy crawler? reading

Tencent cloud database file back solution

Research and application of big data in education industry

Take a look at a clear view of context mapping

Deep Learning technology (NLP) with a PhD from Nanyang Technological University in Singapore

This article has been authorized by the author to Tencent Cloud + community, more original text pleaseClick on the

Search concern public number “cloud plus community”, the first time to obtain technical dry goods, after concern reply 1024 send you a technical course gift package!

Massive technical practice experience, all in the cloud plus community!