我喜欢 Makefile
I Like Makefiles

原始链接: https://switowski.com/blog/i-like-makefiles/

Makefile 是类 Unix 操作系统中常用的通用自动化工具,用于简化各种软件项目的构建、测试和部署过程。 它们的吸引力在于它们在多个项目中的简单性和一致性,以及无论使用何种底层技术都能够执行预定义的命令集。 Makefile 通常定义任务(目标),例如“dev”、“build”、“watch”和“deploy”。 这些任务执行启动开发服务器、编译代码、监视文件修改和发布已完成的项目等操作。 通过使用单个“make”命令,用户可以快速设置和运行这些任务,而无需记住每个单独工具或项目的复杂细节。 虽然与 Docker 或 Gulp 等现代构建工具相比,Makefile 可能显得很基本,但由于它们与多种工具和环境兼容,因此它们提供了灵活性,同时需要最少的设置和更少的依赖项。 由于其悠久的历史、易于学习和跨平台的广泛可用性,它们仍然很受欢迎。

Make 是一种多功能构建自动化工具,可简化从各种输入文件(包括源代码、文档和脚本)创建和更新输出的过程。 虽然最初看起来很简单,但其高级功能为管理复杂项目提供了许多好处。 Make 的一项基本功能是它能够处理“.PHONY”等伪目标,允许用户为不生成对象或二进制文件等传统输出文件的操作声明自定义规则。 用户在定义“安装”、“清理”或特定于项目的目标等目标时可以使用此功能。 尽管 Make 自 Unix 早期就已存在,但某些版本仍然缺少诸如“.PHONY”之类的关键元素。 Make 的现代变体包括“.PHONY”,在创建项目工作流程时提供增强的灵活性和实用性。 当尝试使用 Make 同时生成多个输出文件时,会出现另一个挑战。 在这些场景中,用户必须构建自定义机制来满足他们的需求,例如采用递归规则或通过 make 变量实现额外的抽象层。 虽然 Make 可能会因看似过于简单的初始外观而受到批评,但它的适应性使其能够有效地满足各种需求 - 从琐碎的脚本到涉及不同编程语言和组件的大型项目。 为了方便起见,Makefile 通常用于简洁地概述工作流程,提供文档以及必要的说明。 对 Make 的一项普遍抱怨集中在空白敏感性上。 多年来,许多调试问题都源于此设计缺陷,因此需要仔细注意布局和格式。 此外,Make 对一致构建环境的依赖增加了复杂性,特别是在跨操作系统的命令行标志和行为方面的差异。 开发团队可能会建议 Make 作为通用解决方案,尽管它的适用性因项目规模和复杂性等因素而异。 有时,当特定目录(例如“build”或“dev”)出现在项目根目录中时,Makefile 会停止运行。 尽管如此,Make 对于不需要“更好”构建系统的中等复杂项目仍然有效。 经过仔细检查,Make 展示了非凡的功能和实用性,使其成为简化项目工作流程而不牺牲灵活性或可定制性的有吸引力的选择。
相关文章

原文

I like makefiles. I first used a makefile more than ten years ago. Even back then, it looked like some ancient technology used by the graybeard Linux wizards. Years passed, and new build tools came and went, but I kept seeing makefiles still used here and there. I got used to them because they were part of some projects I joined. At some point, I started to like them. Today, they are often the first automation tool I use when I start a new project.

The reason I like makefiles is that they often follow an unwritten convention of implementing the same set of commands to get you up and running. When I find a project I know nothing about, and I see a Makefile file inside, chances are that I can run make or make build followed by make install, and I will get this project built and set up on my computer. Or at least I will get information on other steps I need to include.

I try to apply the same rule in my projects. If I open a folder with one of my old projects and run make dev, this will perform all the necessary steps to build the project and spin up a dev server. That's convenient because throughout the years, I used many different technologies, and each had different commands to build or deploy a project. I have old projects written in Jekyll, Hugo, 11ty, and all sorts of different Python web frameworks. With makefiles, when I come back to a project I haven't touched for months (or years), I don't have to remember the command to start a dev server with, let's say, Jekyll. I just run make dev, and this, in turn, fires up the corresponding Bundler commands. Even if I use tools like Docker or gulp in my project, I still use makefiles to orchestrate those tools. For example, I often write a make build command that builds all the necessary Docker images, passing additional parameters specific to a given project.

My makefiles are simple. I don't use conditional statements, flags or any other fancy features. Most of the tasks (they are technically called targets, but I always call them tasks in my head) consist of one or more shell commands. I could write bash scripts with a couple of functions instead, but makefiles are easier and faster to write.

Some common tasks that most of my personal projects contain include:

  • dev to start the development server
  • build to build the project (if a build step is necessary)
  • deploy to deploy/publish the project

And that's really it. Sometimes, I include additional tasks like watch to automatically rerun the build task when I change any of the source files. But many of my projects can be managed with just two or three Make commands.

This blog that you're reading right now has a simple makefile with just one target:

dev:
npm run dev

And a more advanced project of mine uses the following makefile to run the dev server, watch for changes, build, encrypt and deploy the website:


dev:
bundle exec jekyll serve --unpublished -w --config _config.yml,_config-dev.yml --livereload


build:
npm run gulp build


watch:
npm run gulp watch -- --wip


deploy:
JEKYLL_ENV=production bundle exec jekyll build; \
make encrypt; \
netlify deploy --prod


encrypt:
npx staticrypt _site/*.html -r -d _site

In both of the above examples, I'm ignoring the existence of phony targets, which you might want to add if you have a file called dev, build, watch, deploy, or encrypt, as many kind readers on Hacker News suggested. Otherwise, this Makefile won't work as expected.

GNU Make (the software that runs makefiles) is quite ubiquitous. If you're on Linux, you probably already have it installed. Even on my MacBook, I don't remember installing it explicitly. It must have come with some other tools that I installed in the past. Make is simple and doesn't require as many additional dependencies as some other build tools. This can be useful if you need a tool that will work in a restricted environment where installing additional packages is difficult or impossible for security reasons. Make will probably be already present in that environment. And if not, you can just take the commands from the makefile and run them manually in the shell. If gulp is not available on your server, you can't really take the JavaScript code and paste that into the terminal.

I'm not against other build tools. I like other build tools too. I'm excited when I find a new one that is better and faster than the one I was using before. But I will still use Make to orchestrate them because it gives me a set of familiar commands to manage all sorts of different setups with different tools.

联系我们 contact @ memedata.com