Browse Source

Links

master
David Larlet 4 weeks ago
parent
commit
05a3b01e4d
Signed by: David Larlet <david@larlet.fr> GPG Key ID: 3E2953A359E7E7BD

+ 195
- 0
cache/2024/2de5f6111316a8158795d70666b55a95/index.html View File

@@ -0,0 +1,195 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>Facebook snooped on users’ Snapchat traffic in secret project, documents reveal (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://techcrunch.com/2024/03/26/facebook-secret-project-snooped-snapchat-user-traffic/">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>Facebook snooped on users’ Snapchat traffic in secret project, documents reveal</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://techcrunch.com/2024/03/26/facebook-secret-project-snooped-snapchat-user-traffic/" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-28
</p>
</nav>
<hr>
<p id="speakable-summary">In 2016, Facebook launched a secret project designed to intercept and decrypt the network traffic between people using Snapchat’s app and its servers. The goal was to understand users’ behavior and help Facebook compete with Snapchat, according to newly unsealed court documents. Facebook called this “Project Ghostbusters,” in a clear reference to Snapchat’s ghost-like logo.</p>
<p>On Tuesday, a federal court in California released new documents discovered as part of the class action lawsuit between consumers and Meta, Facebook’s parent company.</p>
<p>The newly released documents reveal how Meta tried to gain a competitive advantage over its competitors, including Snapchat and later Amazon and YouTube, by analyzing the network traffic of how its users were interacting with Meta’s competitors. Given these apps’ use of encryption, Facebook needed to develop special technology to get around it.</p>
<p><a href="https://www.documentcloud.org/documents/24520332-merged-fb" target="_blank" rel="noopener">One of the documents</a> details Facebook’s Project Ghostbusters. The project was part of the company’s In-App Action Panel (IAPP) program, which used a technique for “intercepting and decrypting” encrypted app traffic from users of Snapchat, and later from users of YouTube and Amazon, the consumers’ lawyers wrote in the document.</p>
<p>The document includes internal Facebook emails discussing the project.</p>
<p>“Whenever someone asks a question about Snapchat, the answer is usually that because their traffic is encrypted we have no analytics about them,” Meta chief executive Mark Zuckerberg wrote in an email dated June 9, 2016, which was published as part of the lawsuit. “Given how quickly they’re growing, it seems important to figure out a new way to get reliable analytics about them. Perhaps we need to do panels or write custom software. You should figure out how to do this.”</p>
<p>Facebook’s engineers solution was to use <a href="https://techcrunch.com/tag/onavo/">Onavo</a>, a VPN-like service that Facebook acquired in 2013. In 2019, <a href="https://techcrunch.com/2019/02/21/facebook-removes-onavo/">Facebook shut down Onavo</a> after a TechCrunch investigation revealed that <a href="https://techcrunch.com/2019/01/29/facebook-project-atlas/">Facebook had been secretly paying teenagers to use Onavo</a> so the company could access all of their web activity.</p>
<p>After Zuckerberg’s email, the Onavo team took on the project and a month later proposed a solution: so-called kits that can be installed on iOS and Android that intercept traffic for specific subdomains, “allowing us to read what would otherwise be encrypted traffic so we can measure in-app usage,” read an email from July 2016. “This is a ‘man-in-the-middle’ approach.”</p>

<p>A man-in-the-middle attack — nowadays also called adversary-in-the-middle — is an attack where hackers intercept internet traffic flowing from one device to another over a network. When the network traffic is unencrypted, this type of attack allows the hackers to read the data inside, such as usernames, passwords, and other in-app activity.</p>
<p>Given that Snapchat encrypted the traffic between the app and its servers, this network analysis technique was not going to be effective. This is why Facebook engineers proposed using Onavo, which when activated had the advantage of reading all of the device’s network traffic before it got encrypted and sent over the internet.</p>
<p>“We now have the capability to measure detailed in-app activity” from “parsing snapchat [sic] analytics collected from incentivized participants in Onavo’s research program,” read another email.</p>
<p>Later, according to the court documents, Facebook expanded the program to Amazon and YouTube.</p>
<p>Inside Facebook, there wasn’t a consensus on whether Project Ghostbusters was a good idea. Some employees, including Jay Parikh, Facebook’s then-head of infrastructure engineering, and Pedro Canahuati, the then-head of security engineering, expressed their concern.</p>
<p>“I can’t think of a good argument for why this is okay. No security person is ever comfortable with this, no matter what consent we get from the general public. The general public just doesn’t know how this stuff works,” Canahuati wrote in an email, included in the court documents.</p>
<p>In 2020, Sarah Grabert and Maximilian Klein <a href="https://www.jurist.org/news/2020/12/class-action-lawsuit-against-facebook-alleges-anticompetitive-behavior/" target="_blank" rel="noopener">filed a class action lawsuit against Facebook</a>, claiming that the company lied about its data collection activities and exploited the data it “deceptively extracted” from users to identify competitors and then unfairly fight against these new companies.</p>
<p>An Amazon spokesperson declined to comment.</p>
<p>Google, Meta, and Snap did not respond to requests for comment.</p>
<p><em>This story was updated to correct the link to the discovery documents in the fourth paragraph.</em></p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 28
- 0
cache/2024/2de5f6111316a8158795d70666b55a95/index.md View File

@@ -0,0 +1,28 @@
title: Facebook snooped on users’ Snapchat traffic in secret project, documents reveal
url: https://techcrunch.com/2024/03/26/facebook-secret-project-snooped-snapchat-user-traffic/
hash_url: 2de5f6111316a8158795d70666b55a95
archive_date: 2024-03-28
og_image: https://techcrunch.com/wp-content/uploads/2024/03/mark-zuckerberg-meta-facebook-snapchat-project-ghostbusters.jpg?resize=1200,800
description: A secret program called "Project Ghostbusters" saw Facebook devise a way to intercept and decrypt the encrypted network traffic of Snapchat users to study their behavior.
favicon: https://techcrunch.com/wp-content/uploads/2015/02/cropped-cropped-favicon-gradient.png?w=32
language: en_US

<p id="speakable-summary">In 2016, Facebook launched a secret project designed to intercept and decrypt the network traffic between people using Snapchat’s app and its servers. The goal was to understand users’ behavior and help Facebook compete with Snapchat, according to newly unsealed court documents. Facebook called this “Project Ghostbusters,” in a clear reference to Snapchat’s ghost-like logo.</p>
<p>On Tuesday, a federal court in California released new documents discovered as part of the class action lawsuit between consumers and Meta, Facebook’s parent company.</p>
<p>The newly released documents reveal how Meta tried to gain a competitive advantage over its competitors, including Snapchat and later Amazon and YouTube, by analyzing the network traffic of how its users were interacting with Meta’s competitors. Given these apps’ use of encryption, Facebook needed to develop special technology to get around it.</p>
<p><a href="https://www.documentcloud.org/documents/24520332-merged-fb" target="_blank" rel="noopener">One of the documents</a> details Facebook’s Project Ghostbusters. The project was part of the company’s In-App Action Panel (IAPP) program, which used a technique for “intercepting and decrypting” encrypted app traffic from users of Snapchat, and later from users of YouTube and Amazon, the consumers’ lawyers wrote in the document.</p>
<p>The document includes internal Facebook emails discussing the project.</p>
<p>“Whenever someone asks a question about Snapchat, the answer is usually that because their traffic is encrypted we have no analytics about them,” Meta chief executive Mark Zuckerberg wrote in an email dated June 9, 2016, which was published as part of the lawsuit. “Given how quickly they’re growing, it seems important to figure out a new way to get reliable analytics about them. Perhaps we need to do panels or write custom software. You should figure out how to do this.”</p>
<p>Facebook’s engineers solution was to use <a href="https://techcrunch.com/tag/onavo/">Onavo</a>, a VPN-like service that Facebook acquired in 2013. In 2019, <a href="https://techcrunch.com/2019/02/21/facebook-removes-onavo/">Facebook shut down Onavo</a> after a TechCrunch investigation revealed that <a href="https://techcrunch.com/2019/01/29/facebook-project-atlas/">Facebook had been secretly paying teenagers to use Onavo</a> so the company could access all of their web activity.</p>
<p>After Zuckerberg’s email, the Onavo team took on the project and a month later proposed a solution: so-called kits that can be installed on iOS and Android that intercept traffic for specific subdomains, “allowing us to read what would otherwise be encrypted traffic so we can measure in-app usage,” read an email from July 2016. “This is a ‘man-in-the-middle’ approach.”</p>
<p>A man-in-the-middle attack — nowadays also called adversary-in-the-middle — is an attack where hackers intercept internet traffic flowing from one device to another over a network. When the network traffic is unencrypted, this type of attack allows the hackers to read the data inside, such as usernames, passwords, and other in-app activity.</p>
<p>Given that Snapchat encrypted the traffic between the app and its servers, this network analysis technique was not going to be effective. This is why Facebook engineers proposed using Onavo, which when activated had the advantage of reading all of the device’s network traffic before it got encrypted and sent over the internet.</p>
<p>“We now have the capability to measure detailed in-app activity” from “parsing snapchat [sic] analytics collected from incentivized participants in Onavo’s research program,” read another email.</p>
<p>Later, according to the court documents, Facebook expanded the program to Amazon and YouTube.</p>
<p>Inside Facebook, there wasn’t a consensus on whether Project Ghostbusters was a good idea. Some employees, including Jay Parikh, Facebook’s then-head of infrastructure engineering, and Pedro Canahuati, the then-head of security engineering, expressed their concern.</p>
<p>“I can’t think of a good argument for why this is okay. No security person is ever comfortable with this, no matter what consent we get from the general public. The general public just doesn’t know how this stuff works,” Canahuati wrote in an email, included in the court documents.</p>
<p>In 2020, Sarah Grabert and Maximilian Klein <a href="https://www.jurist.org/news/2020/12/class-action-lawsuit-against-facebook-alleges-anticompetitive-behavior/" target="_blank" rel="noopener">filed a class action lawsuit against Facebook</a>, claiming that the company lied about its data collection activities and exploited the data it “deceptively extracted” from users to identify competitors and then unfairly fight against these new companies.</p>
<p>An Amazon spokesperson declined to comment.</p>
<p>Google, Meta, and Snap did not respond to requests for comment.</p>
<p><em>This story was updated to correct the link to the discovery documents in the fourth paragraph.</em></p>

+ 182
- 0
cache/2024/422adbcbb23d5d87eb9d9f07f21e6e52/index.html View File

@@ -0,0 +1,182 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>Growth is a mind cancer (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://manuelmoreale.com/growth-is-a-mind-cancer">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>Growth is a mind cancer</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://manuelmoreale.com/growth-is-a-mind-cancer" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-28
</p>
</nav>
<hr>
<p>I'm following with somewhat vague interest the various legal battles Apple is currently involved in. Reading their response to the EU's DMA makes me sad. Not for the company itself. I honestly could not care less about the company. Nor for the people who run that company. I'm sad because the pursuit of endless growth is such a mind cancer. It consumes and distracts everyone. If you're an artisan, creating amazing objects is your end goal. Ideally, you want those objects to last forever. And if they don't, you want to do such an amazing job that once something is broken beyond repair, people will come to you again and ask you to make something new, rather than buying from someone else.</p>
<p>Apple makes amazing products. I bought the laptop I'm typing this 9 years ago. It still works fine. Sure, it's slow compared to my new machine but I can use it to do calls and write blog posts. And that's great. I love it. I was happy to give Apple my money back in 2015. But you know who's not happy? Apple. Apple is not happy that I bought a laptop in 2015 that was so good that it is still working fine 9 years later. And it's also not happy that I bought a phone more than 4 years ago that still does all the things I need it to do. Because they need to make money. More money. There's no end state here. <em>"More"</em> has no end state. At some point, a company like Apple will inevitably run out of people willing to buy their stuff. Because it's unreasonable to expect people to upgrade phones, laptops, screens, watches, tablets, virtual-ski-goggles every damn year. And so what do they do? They move into services. Music, movies, games, fitness, storage. You name it. But those also can't grow forever. Because guess what? There are other companies out there doing the same.</p>
<p>But they can't stop. They're a public company. If they're not growing enough it means they're failing. Forget that they make amazing products that can last decades with no issues. Forget that they're an almost 3-fucking-trillions dollar company. If they're not growing enough, stock goes down and that's no good. Because remember, there's no finish line here. They can't just be happy with their size. They can't be happy with the idea of employing thousands of smart people and creating amazing products. No, they have to keep growing. And sooner or later, this mind cancer becomes malignant.</p>
<p>Don't get me wrong, this is not just an Apple issue. It's an issue with any big company. It's an issue with everyone who can't accept that they reached their end state.</p>
<p>Cory Doctorow famously coined the enshittification term to describe the sad trend of online services going to shit over time. I don't think that's just an online services issue. It's a societal issue related to the pursuit of endless growth. And if you think about it, it's a deeply human issue. It's what happens when you can't say stop. No matter what you're doing, it can be something positive or negative, if you can't say stop, bad things will happen. Try to go for a run, and don't stop. Ever. Or try to drink water, and don't stop. Ever.</p>
<p>But it's our fault. Our as a society. We celebrate when Apple becomes the first trillion-dollar company but we don't celebrate when someone says <em>"You know what? I think I have enough"</em>.</p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 15
- 0
cache/2024/422adbcbb23d5d87eb9d9f07f21e6e52/index.md View File

@@ -0,0 +1,15 @@
title: Growth is a mind cancer
url: https://manuelmoreale.com/growth-is-a-mind-cancer
hash_url: 422adbcbb23d5d87eb9d9f07f21e6e52
archive_date: 2024-03-28
og_image:
description: I'm following with somewhat vague interest the various legal battles Apple is currently involved in. Reading their response to the EU's DMA makes me …
favicon: https://manuelmoreale.com/favicon.ico
language: en_US

<p>I'm following with somewhat vague interest the various legal battles Apple is currently involved in. Reading their response to the EU's DMA makes me sad. Not for the company itself. I honestly could not care less about the company. Nor for the people who run that company. I'm sad because the pursuit of endless growth is such a mind cancer. It consumes and distracts everyone. If you're an artisan, creating amazing objects is your end goal. Ideally, you want those objects to last forever. And if they don't, you want to do such an amazing job that once something is broken beyond repair, people will come to you again and ask you to make something new, rather than buying from someone else.</p>
<p>Apple makes amazing products. I bought the laptop I'm typing this 9 years ago. It still works fine. Sure, it's slow compared to my new machine but I can use it to do calls and write blog posts. And that's great. I love it. I was happy to give Apple my money back in 2015. But you know who's not happy? Apple. Apple is not happy that I bought a laptop in 2015 that was so good that it is still working fine 9 years later. And it's also not happy that I bought a phone more than 4 years ago that still does all the things I need it to do. Because they need to make money. More money. There's no end state here. <em>"More"</em> has no end state. At some point, a company like Apple will inevitably run out of people willing to buy their stuff. Because it's unreasonable to expect people to upgrade phones, laptops, screens, watches, tablets, virtual-ski-goggles every damn year. And so what do they do? They move into services. Music, movies, games, fitness, storage. You name it. But those also can't grow forever. Because guess what? There are other companies out there doing the same.</p>
<p>But they can't stop. They're a public company. If they're not growing enough it means they're failing. Forget that they make amazing products that can last decades with no issues. Forget that they're an almost 3-fucking-trillions dollar company. If they're not growing enough, stock goes down and that's no good. Because remember, there's no finish line here. They can't just be happy with their size. They can't be happy with the idea of employing thousands of smart people and creating amazing products. No, they have to keep growing. And sooner or later, this mind cancer becomes malignant.</p>
<p>Don't get me wrong, this is not just an Apple issue. It's an issue with any big company. It's an issue with everyone who can't accept that they reached their end state.</p>
<p>Cory Doctorow famously coined the enshittification term to describe the sad trend of online services going to shit over time. I don't think that's just an online services issue. It's a societal issue related to the pursuit of endless growth. And if you think about it, it's a deeply human issue. It's what happens when you can't say stop. No matter what you're doing, it can be something positive or negative, if you can't say stop, bad things will happen. Try to go for a run, and don't stop. Ever. Or try to drink water, and don't stop. Ever.</p>
<p>But it's our fault. Our as a society. We celebrate when Apple becomes the first trillion-dollar company but we don't celebrate when someone says <em>"You know what? I think I have enough"</em>.</p>

+ 548
- 0
cache/2024/821fa933883f080d23c0a6d9d0b3721a/index.html View File

@@ -0,0 +1,548 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="fr">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>Make (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://www.arthurperret.fr/cours/make.html">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>Make</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://www.arthurperret.fr/cours/make.html" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-28
</p>
</nav>
<hr>
<h1 data-number="1"> Qu’est-ce que Make ?</h1>
<p><a href="https://www.gnu.org/software/make/">Make</a> fait partie des logiciels d’automatisation des tâches : il sert à créer des processus dans lesquels des actions s’enchaînent automatiquement.</p>
<p>À l’origine, Make est un outil conçu pour le développement de logiciels : il sert à automatiser la fabrication de programmes exécutables à partir de fichiers contenant du code.</p>
<p>Mais en réalité, Make peut être utilisé pour gérer toutes sortes de projets. En tant que langage, il permet de définir des processus de manière très simple (encore plus simple que les <a href="ligne-de-commande.html#scripts-shell">scripts shell</a>). Et en tant qu’outil, il permet de gagner du temps, avec une gestion efficace des états des fichiers qui fait économiser du travail aussi bien à l’humain qu’à la machine.</p>
<section id="origine-gerer-la-fabrication-de-programmes-informatiques" class="level2" data-number="1.1">
<h2 data-number="1.1"> Origine : gérer la fabrication de programmes informatiques</h2>
<p>Make a été inventé pour automatiser la fabrication de programmes exécutables. Un programme exécutable est un fichier qui se fabrique en deux temps : d’abord, on écrit du code dans des fichiers texte ; puis on exécute un programme appelé compilateur qui transforme le code en un fichier exécutable – cette deuxième étape, c’est la compilation.</p>
<p>Make a été conçu en particulier pour déterminer quels fichiers doivent être recompilés lors de la fabrication d’un programme. Lorsqu’un fichier est modifié, il déclenche automatiquement les commandes appropriées. L’objectif était de ne pas tout recompiler si on ne modifiait qu’une petite partie du code d’un programme complexe, afin d’économiser des ressources et gagner du temps.</p>
<p>Pour faire cela, Make s’appuie sur deux choses : la date de dernière modification des fichiers ; et un arbre de dépendances, c’est-à-dire une représentation des relations entre des fichiers cible, qu’on veut fabriquer, et des fichiers source (les « dépendances »), qui sont nécessaires à la fabrication des fichiers cible. Évidemment, un fichier cible peut à son tour être fichier source pour fabriquer un autre fichier cible, et ainsi de suite.</p>
<p>Voici un exemple d’arbre de dépendances :</p>
<figure>
<img src="../img/make-arbre-dependances.svg" alt="Le programme « main.cpp » dépend de quatre fichiers, dont deux dépendent eux-mêmes d’autres fichiers. Diagramme inspiré de makefiletutorial.com">
<figcaption aria-hidden="true">Le programme « main.cpp » dépend de quatre fichiers, dont deux dépendent eux-mêmes d’autres fichiers. Diagramme inspiré de <a href="https://makefiletutorial.com/">makefiletutorial.com</a></figcaption>
</figure>
<p>Si l’une des dépendances change, Make recompile le ou les fichiers cible correspondants. Dans l’exemple ci-dessus, si <code>one.h</code> est modifié, Make recompile <code>one.cpp</code> puis <code>main.cpp</code>, mais pas <code>two.cpp</code>.</p>
</section>
<section id="fonctionnement-les-makefiles" class="level2" data-number="1.2">
<h2 data-number="1.2"> Fonctionnement : les Makefiles</h2>
<p>Le fonctionnement de Make repose sur des fichiers texte appelés <em>Makefiles</em>. Dans un Makefile, on rédige des instructions appelées <strong>règles</strong> (<em>rules</em>) ; chaque règle définit une <strong>recette</strong> (<em>recipe</em>) pour fabriquer une ou plusieurs <strong>cibles</strong> (<em>targets</em>) suivant des <strong>prérequis</strong> (<em>prerequisites</em>).</p>
<p>Les règles s’écrivent de la manière suivante :</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dv">cibles :</span><span class="dt"> prérequis</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> recette</span></code></pre></div>
<p>Ce sont les relations entre cibles et prérequis qui constituent l’arbre de dépendances modélisé par Make.</p>
<p>Une recette est constituée d’une ou plusieurs <strong>commandes</strong> : des instructions textuelles qu’on exécuterait habituellement dans un terminal.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dv">cibles :</span><span class="dt"> prérequis</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> commande</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> commande</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> commande</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> …</span></code></pre></div>
<p>Comme dans de nombreux langages informatiques, la syntaxe des Makefiles inclut la possibilité de créer des variables, de laisser des commentaires, de définir des fonctions qui incluent des étapes logiques, etc.</p>
<p>Pour exécuter les instructions contenues dans un Makefile, il faut ouvrir un terminal, se déplacer à l’emplacement du Makefile, puis exécuter la commande <code>make</code> (ce qui déclenche les instructions qui sont contenues dans le Makefile).</p>
<p>Si vous souhaitez découvrir comment utiliser un terminal, consultez ma page <a href="ligne-de-commande.html" class="cours">Ligne de commande</a>.</p>
</section>
<section id="utilisation-en-dehors-du-developpement-logiciel" class="level2" data-number="1.3">
<h2 data-number="1.3"> Utilisation en dehors du développement logiciel</h2>
<p>Progressivement, d’autres usages de Make ont émergé. En effet, son fonctionnement a une portée plus générale que la seule fabrication de programmes, car il ne pose aucune restriction sur la nature de la cible, des prérequis ou de la recette. On peut utiliser Make pour automatiser le déclenchement de n’importe quel programme qui s’utilise habituellement à la ligne de commande.</p>
<p>Donc si Make est souvent présenté comme un outil d’automatisation pour la <em>compilation de programmes</em>, il serait en fait plus juste de le décrire comme un outil d’automatisation pour la <em>ligne de commande</em>. C’est un outil qui permet de définir des enchaînements de commandes textuelles ; dit de façon plus abstraite, Make permet de créer des processus, des séquences d’actions, où chaque action est une instruction envoyée à un ou plusieurs programmes à interface textuelle. Bref : Make permet de fabriquer des fabriques. (Clin d’œil à Antoine Fauchié qui a beaucoup travaillé sur ce concept de fabrique, voir <a href="https://www.quaternum.net/2023/06/02/fabrique-concept/">son blog</a> et <a href="https://these.quaternum.net/concepts/#fabrique">sa thèse</a>.)</p>
<div class="exemple">
<p>J’ai partagé un modèle de Makefile qui automatise l’utilisation de <a href="pandoc.html">Pandoc</a> pour générer différents exports à partir d’une même source : <a href="https://github.com/infologie/pandoc-ssp">Pandoc-SSP</a>. Make a complètement remplacé mon usage des scripts shell pour automatiser le déclenchement de commandes Pandoc. J’utiliserai Pandoc comme exemple à plusieurs reprises sur cette page.</p>
<p>Make me sert donc à rédiger des recettes dans lesquelles je passe d’ingrédients de base (des fichiers) à un résultat final (d’autres fichiers). Mais comme me l’a fait remarquer David Larlet, on peut aussi l’utiliser pour des processus qui n’ont pas forcément de fichiers en entrée ou en sortie, comme la mise en place d’un serveur ou bien l’installation d’un logiciel. Make est un outil versatile.</p>
</div>
</section>
<section id="outils-requis-pour-utiliser-make" class="level2" data-number="1.4">
<h2 data-number="1.4"> Outils requis pour utiliser Make</h2>
<p>Pour utiliser Make, vous avez besoin des choses suivantes :</p>
<dl>
<dt>Un terminal</dt>
<dd>
Si vous n’êtes pas familier de ce type d’environnement, consultez ma page <a href="ligne-de-commande.html" class="cours">Ligne de commande</a>.
</dd>
<dt>Le programme Make</dt>
<dd>
Make existe dans plusieurs versions. Cette page est écrite en référence à <a href="https://www.gnu.org/software/make/manual/">GNU Make</a>, la version la plus répandue. Sur Linux et macOS, Make est pré-installé ; pour vérifier de quelle version vous disposez, exécutez <code>make --version</code> dans un terminal. Sur Windows, vous pouvez installer Make par exemple en installant le gestionnaire de programmes <a href="https://chocolatey.org/install">Chocolatey</a>, puis en utilisant Chocolatey pour installer Make en ouvrant un terminal (je recommande PowerShell) et en exécutant la commande <code>choco install make</code>.
</dd>
<dt>Un éditeur de texte</dt>
<dd>
Pour rédiger des Makefiles. Je recommande souvent <a href="https://notepad-plus-plus.org">Notepad++</a> (Windows), <a href="http://www.barebones.com/products/bbedit/index.html">BBEdit</a> (macOS) et <a href="https://doc.ubuntu-fr.org/gedit">gedit</a> (Linux), mais n’importe quel éditeur de texte fera l’affaire.
</dd>
</dl>
</section>
</section>
<section id="prise-en-main-ecrire-un-makefile-et-executer-make" class="level1" data-number="2">
<h1 data-number="2"> Prise en main : écrire un Makefile et exécuter Make</h1>
<p>Make s’utilise en écrivant des Makefiles et en utilisant la commande <code>make</code> dans un terminal. Par défaut, <code>make</code> cherche un fichier appelé <code>makefile</code> ou <code>Makefile</code> situé dans le répertoire courant. On peut aussi nommer un Makefile par n’importe quel <code>nom</code> et utiliser <code>make</code> avec l’option <code>--file=nom</code> ou <code>-f nom</code>.</p>
<section id="regles-cibles-prerequis-recettes" class="level2" data-number="2.1">
<h2 data-number="2.1"> Règles : cibles, prérequis, recettes</h2>
<p>Un Makefile contient une ou plusieurs règles.</p>
<p>La syntaxe générale d’une règle est la suivante :</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dv">cibles :</span><span class="dt"> prérequis</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> recette</span></code></pre></div>
<div class="important">
<p>Notez l’espacement avant le mot « recette » : dans une règle, <strong>la recette doit être indentée avec une tabulation</strong> (une seule tabulation, pas d’espaces).</p>
<p>(Si comme moi vous utilisez régulièrement YAML, un langage qui requiert d’indenter les lignes avec des espaces, préparez-vous à vous mélanger les pinceaux pendant quelques temps !)</p>
</div>
<p>Les <strong>cibles</strong> et les <strong>prérequis</strong> sont des noms de fichiers, séparés par des espaces. En pratique, il n’y a souvent qu’une cible par règle.</p>
<p>La <strong>recette</strong> est constituée d’une ou plusieurs <strong>commandes</strong>, qui sont une série d’étapes utilisées pour créer la cible. Make applique la recette :</p>
<ul>
<li>si les cibles n’existent pas ;</li>
<li>si les cibles existent mais que les prérequis ont une date de dernière modification plus récente que les cibles.</li>
</ul>
<div class="exemple">
<p>Dans le Makefile suivant, la cible est un fichier PDF, <code>doc.pdf</code>. Elle a un prérequis qui est un fichier texte rédigé en Markdown, <code>doc.md</code>. La recette est constituée par une commande qui exécute le programme Pandoc.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dv">doc.pdf :</span><span class="dt"> doc.md</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> pandoc doc.md -o doc.pdf</span></code></pre></div>
<p>Si j’exécute <code>make</code> :</p>
<ul>
<li>Si <code>doc.pdf</code> n’existe pas, ou qu’il existe mais que <code>doc.md</code> est plus récent (donc qu’il a été modifié depuis la dernière fabrication de <code>doc.pdf</code>), Make lance la commande Pandoc qui convertit le fichier Markdown en PDF.</li>
<li>Si <code>doc.pdf</code> existe et que <code>doc.md</code> n’est pas plus récent, rien ne se passe.</li>
</ul>
</div>
</section>
<section id="cibles-factices" class="level2" data-number="2.2">
<h2 data-number="2.2"> Cibles factices</h2>
<p>Les cibles peuvent être des cibles « factices » (en anglais <em>phony</em>). Une cible factice ne correspond pas à un fichier réel : c’est une sorte de nom de code pour une série d’opérations qu’on veut exécuter. Les cibles factices doivent être déclarées comme prérequis de la cible spéciale <code>.PHONY</code>.</p>
<div class="exemple">
<p>Je reprends l’exemple précédent, avec cette fois deux fichiers Markdown, <code>doc-fr.md</code> et <code>doc-en.md</code>. Je définis une cible <code>all</code> qui a deux prérequis, les cibles <code>doc-fr.pdf</code> et <code>doc-en.pdf</code>.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-fr.pdf :</span><span class="dt"> doc-fr.md</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> pandoc doc-fr.md -o doc-fr.pdf</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-en.pdf :</span><span class="dt"> doc-en.md</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> pandoc doc-en.md -o doc-en.pdf</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span></code></pre></div>
<p>Si j’exécute <code>make all</code>, Make déclenchera la fabrication des deux cibles correspondantes, à moins que les cibles existent déjà et qu’elles soient plus récentes que les prérequis.</p>
</div>
</section>

<section id="ordre-dexecution-des-regles" class="level2" data-number="2.4">
<h2 data-number="2.4"> Ordre d’exécution des règles</h2>
<p>Si un Makefile contient plusieurs règles, par défaut <code>make</code> fabrique la première cible qu’il trouve dans le fichier. Il est possible de définir manuellement la cible à exécuter par défaut grâce à la variable spéciale <code>.DEFAULT_GOAL</code> (voir la section <a href="#variables-speciales">Variables spéciales</a> plus bas).</p>
</section>
<section id="options-dexecution" class="level2" data-number="2.5">
<h2 data-number="2.5"> Options d’exécution</h2>
<p>La commande <code>make</code> dispose de nombreuses options qui modifient son comportement. Utilisez <code>make --option</code> pour appliquer l’option correspondante lors de l’utilisation de Make. Plusieurs options peuvent être utilisées simultanément. La plupart des options sont disponibles sous plusieurs noms (notamment pour des questions de compatibilité), dont des formes raccourcies avec un seul tiret et une seule lettre, pour aller plus vite lorsqu’on saisit les commandes manuellement.</p>
<p>Quelques exemples :</p>
<ul>
<li><code>-n</code> (ou <code>--just-print</code>, <code>--dry-run</code>, <code>--recon</code>) : affiche les commandes qui seraient exécutées, sans les exécuter. Ceci permet de vérifier ce que ferait un Makefile sans l’appliquer réellement.</li>
<li><code>-j [jobs]</code> (ou <code>--jobs[=jobs]</code>) : autorise Make à exécuter simultanément un certain nombre de commandes (<code>jobs</code>). Si votre machine dispose d’un processeurs avec plusieurs cœurs, ceci permet de réduire considérablement le temps d’exécution du Makefile. Par exemple, avec un processeur à huit cœurs, vous pouvez utiliser l’option <code>-j 8</code> pour que Make lance jusqu’à huit commandes en parallèle (une par cœur).</li>
</ul>
<p>Dans un terminal, utilisez <code>man make</code> pour afficher le manuel et découvrir les options. Appuyez sur la touche <kbd>q</kbd> pour quitter le manuel.</p>
</section>
<section id="supprimer-lecho" class="level2" data-number="2.6">
<h2 data-number="2.6"> Supprimer l’écho</h2>
<p>Par défaut, Make affiche dans le terminal chaque ligne d’une recette exécutée. On appelle cela « l’écho », par analogie avec la commande <code>echo</code> qu’on utilise dans un terminal pour afficher des messages.</p>
<p>Il y a plusieurs manières de supprimer l’écho :</p>
<ul>
<li>On peut préfixer une ligne par une arobase <code>@</code> pour supprimer l’écho pour cette ligne.</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb7"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-fr.pdf :</span><span class="dt"> doc-fr.md</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="ch">@</span><span class="fu">pandoc doc-fr.md -o doc-fr.pdf</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-en.pdf :</span><span class="dt"> doc-en.md</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="ch">@</span><span class="fu">pandoc doc-en.md -o doc-en.pdf</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span></code></pre></div>
</div>
<p>Remarque : il existe d’autres préfixes qu’on peut ajouter à une ligne pour modifier le comportement de Make. Par exemple, préfixer une ligne par <code>-</code> indique à Make qu’il ne doit pas s’interrompre en cas d’erreur, ce qui est pratique lorsqu’on dépanne un processus.</p>
<ul>
<li>On peut utiliser la cible spéciale <code>.SILENT</code> en indiquant des prérequis, ce qui supprimera l’écho pour la fabrication des prérequis correspondants.</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb8"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ot">.SILENT:</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-fr.pdf :</span><span class="dt"> doc-fr.md</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> pandoc doc-fr.md -o doc-fr.pdf</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-en.pdf :</span><span class="dt"> doc-en.md</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> pandoc doc-en.md -o doc-en.pdf</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="co"># "make all" sera silencieux aussi !</span></span></code></pre></div>
</div>
<p>Remarque : <code>.PHONY</code> et <code>.SILENT</code> sont deux exemples de cibles spéciales. Voir <a href="https://www.gnu.org/software/make/manual/make.html#Special-Targets">Special Targets</a> dans le manuel GNU Make pour la liste complète.</p>
<ul>
<li>On peut utiliser l’option <code>-s</code> (ou <code>--silent</code>, <code>--quiet</code>) au moment d’exécuter la commande <code>make</code>. Toutes les recettes s’exécuteront alors silencieusement.</li>
</ul>
</section>
</section>
<section id="variables" class="level1" data-number="3">
<h1 data-number="3"> Variables</h1>
<p>Make permet de créer des variables, c’est-à-dire d’affecter une valeur à un nom pour pouvoir ensuite utiliser le nom à la place de la valeur. L’intérêt premier des variables, c’est d’éviter d’écrire plusieurs fois la même chose, pour économiser du travail et réduire le risque de faire des erreurs.</p>
<p>Pour définir une variable :</p>

<p>Pour utiliser la valeur de cette variable :</p>

<div class="exemple">
<p>Dans le Makefile ci-dessous, une longue liste de fichiers apparaît plusieurs fois :</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="dv">edit :</span><span class="dt"> main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="dv">clean :</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span></code></pre></div>
<p>On peut définir une variable dont la valeur est cette liste de fichiers, puis utiliser le nom de la variable partout où cette liste doit être utilisée :</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="dt">objects</span> <span class="ch">=</span><span class="st"> main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="dv">edit :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">objects</span><span class="ch">)</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> cc -o edit <span class="ch">$(</span><span class="dt">objects</span><span class="ch">)</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="dv">clean :</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> rm edit <span class="ch">$(</span><span class="dt">objects</span><span class="ch">)</span></span></code></pre></div>
</div>
<p>Une variable peut être redéfinie, c’est-à-dire qu’on peut la définir à nouveau dans le même Makefile. La nouvelle valeur remplace alors la précédente.</p>
<div class="exemple">
<div class="sourceCode" id="cb13"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> Bonjour !</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> Bonsoir !</span></span></code></pre></div>
</div>
<p>La valeur d’une variable peut elle-même contenir une ou plusieurs variables.</p>
<div class="exemple">
<div class="sourceCode" id="cb14"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">txt</span> <span class="ch">=</span><span class="st"> doc1.txt doc2.txt</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="dt">img</span> <span class="ch">=</span><span class="st"> soleil.svg lune.svg</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="dt">fichiers</span><span class="ch"> =</span><span class="st"> </span><span class="ch">$(</span><span class="dt">txt</span><span class="ch">)</span><span class="st"> </span><span class="ch">$(</span><span class="dt">img</span><span class="ch">)</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "fichiers" est "doc1.txt doc2.txt soleil.svg lune.svg"</span></span></code></pre></div>
</div>
<div class="important">
<p>La plupart des langages informatiques reconnaissent plusieurs types de données, soit de manière implicite, soit de manière explicite grâce à l’ajout de caractères spéciaux. Exemple en JavaScript :</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> <span class="op">=</span> <span class="kw">true</span> <span class="co">// booléen (boolean)</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> <span class="op">=</span> <span class="dv">28</span> <span class="co">// nombre entier (integer)</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> <span class="op">=</span> <span class="st">"Bonjour !"</span> <span class="co">// chaîne de caractères (string)</span></span></code></pre></div>
<p><strong>Make ne reconnaît qu’un type de données : la chaîne de caractères</strong> (en anglais : <em>string</em>). Par conséquent, il interprète les guillemets littéralement. Il est donc conseillé de n’utiliser les guillemets que lorsque la chaîne va être interprétée dans un autre environnement, par exemple lorsqu’on passe une variable au shell pour exécuter une commande.</p>
<p>Ainsi, plutôt que :</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> "Bonjour !"</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="dv">test :</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> echo <span class="ch">$(</span><span class="dt">message</span><span class="ch">)</span></span></code></pre></div>
<p>On écrira :</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> Bonjour !</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="dv">test :</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> echo <span class="st">"</span><span class="ch">$(</span><span class="dt">message</span><span class="ch">)</span><span class="st">"</span></span></code></pre></div>
</div>
<section id="expansion-et-operateurs-de-definition" class="level2" data-number="3.1">
<h2 data-number="3.1"> Expansion et opérateurs de définition</h2>
<p>On parle d’expansion pour désigner le fait de remplacer une expression de la forme <code>$(nom)</code> par la valeur de la variable correspondante.</p>
<p>Ce concept devient important lorsqu’on définit des variables qui contiennent d’autres variables, et plus encore lorsque des variables sont redéfinies.</p>
<p>En effet, Make permet de gérer de manière très fine l’ordre dans lequel se passe l’expansion. Ceci repose sur l’utilisation de différents opérateurs de définition (voir la section <a href="https://www.gnu.org/software/make/manual/make.html#Reading-Makefiles">How Make reads a Makefile</a> du manuel de GNU Make pour la liste des opérateurs et leurs effets). Or les différences de comportement entre ces opérateurs sont souvent source de confusion.</p>
<p>Les deux opérateurs qu’on rencontre le plus fréquemment sont <code>=</code> et <code>:=</code>.</p>
<ul>
<li>Quand on utilise <code>=</code> pour définir une variable, l’expansion des variables contenues dans la valeur se fait au moment où la variable est <strong>utilisée</strong>. Ceci permet de changer la valeur en cours de route, si on redéfinit les variables utilisées dans la valeur.</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb18"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonjour</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="dt">hello</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">mot</span><span class="ch">)</span><span class="st"> à vous !</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est "bonjour à vous !"</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="co"># on redéfinit "mot" en changeant sa valeur</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonsoir</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est maintenant "bonsoir à vous !"</span></span></code></pre></div>
</div>
<ul>
<li>Quand on utilise <code>:=</code> pour définir une variable, l’expansion des variables contenues dans la valeur se fait au moment où la variable est <strong>définie</strong>. Ceci permet de fixer la valeur une fois pour toutes. Exemple :</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb19"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonjour</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="dt">hello</span><span class="ch"> :=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">mot</span><span class="ch">)</span><span class="st"> à vous !</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est "bonjour à vous !"</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="co"># on redéfinit "mot"</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonsoir</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" reste "bonjour à vous !"</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a><span class="co"># on redéfinit cette fois "hello", on répète la première définition</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="dt">hello</span><span class="ch"> :=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">mot</span><span class="ch">)</span><span class="st"> à vous !</span></span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est maintenant "bonsoir à vous !"</span></span></code></pre></div>
</div>
<p>En résumé :</p>
<ul>
<li>Si on définit des variables qui contiennent des variables, et qu’on modifie ces définitions en cours de route, alors on peut alterner entre <code>=</code> et <code>:=</code> suivant les besoins.</li>
<li>En revanche, lorsqu’une variable contient une valeur simple (on parle aussi de valeur littérale), c’est-à-dire qu’elle ne contient pas elle-même de variable, alors utiliser <code>=</code> ou <code>:=</code> ne fait <strong>aucune différence</strong>, puisqu’il n’y a rien dans la valeur de la variable définie qui nécessite une expansion.</li>
<li>Et lorsque le Makefile ne contient que des variables définies une seule fois, alors utiliser <code>=</code> ou <code>:=</code> ne fait <strong>aucune différence non plus</strong>, puisqu’il n’y a aucune redéfinition qui vient affecter l’expansion.</li>
</ul>
</section>
<section id="variables-speciales" class="level2" data-number="3.2">
<h2 data-number="3.2"> Variables spéciales</h2>
<p>Make inclut quelques variables <em>spéciales</em>, dont le nom est réservé pour un comportement prédéfini.</p>
<p>Par exemple, lorsqu’on exécute <code>make</code> sans argument, c’est la première règle contenue dans le Makefile qui est appliquée. On peut utiliser la variable spéciale <code>.DEFAULT_GOAL</code> pour définir la règle à appliquer par défaut.</p>
<div class="exemple">
<p>Avec le Makefile ci-dessous, exécuter <code>make</code> fabriquera <code>doc.html</code> au lieu de <code>all</code> :</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="dt">.DEFAULT_GOAL</span><span class="ch"> :=</span><span class="st"> doc.html</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc.pdf doc.html</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a><span class="dv">doc.pdf :</span><span class="dt"> doc.md</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a> pandoc doc.md -o doc.pdf</span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a><span class="dv">doc.html :</span><span class="dt"> doc.md</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a> pandoc doc.md -o doc.html</span></code></pre></div>
</div>
<p>Voir <a href="https://www.gnu.org/software/make/manual/make.html#Special-Variables">Special Variables</a> dans le manuel GNU Make pour la liste complète.</p>
</section>
</section>
<section id="fonctions" class="level1" data-number="5">
<h1 data-number="5"> Fonctions</h1>
<p>Make inclut la possibilité d’utiliser des fonctions pour manipuler du texte. Il y a des fonctions prédéfinies qui correspondent aux opérations les plus courantes : trier, chercher, filtrer, tronquer, etc. Il est également possible de définir ses propres fonctions.</p>
<p>Une fonction s’utilise en faisant appel à son nom et en lui passant du texte en argument :</p>

<div class="exemple">
<ul>
<li>La fonction <code>$(wildcard pattern)</code> cherche les noms de fichiers qui correspondent au motif <code>pattern</code>.</li>
<li>La fonction de référence avec substitution <code>$(var:a=b)</code> remplace la chaîne de caractères <code>a</code> par <code>b</code> dans la variable <code>var</code>.</li>
</ul>
<p>Dans l’exemple ci-dessous, on combine ces deux fonctions pour créer une variable <code>input</code> qui contient les noms de fichiers Markdown présents dans le répertoire où se situe le Makefile, puis une variable <code>output</code> qui contient les mêmes noms en remplaçant l’extension <code>.md</code> par <code>.html</code>.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="dt">input</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">wildcard</span><span class="st"> *.md</span><span class="ch">)</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="dt">output</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">input</span><span class="kw">:</span><span class="ss">.md</span><span class="kw">=</span><span class="ss">.html</span><span class="ch">)</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="co"># si input a pour valeur par exemple "doc.md 2024-03-25.md"</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a><span class="co"># output aura pour valeur "doc.html 2024-03-25.html"</span></span></code></pre></div>
</div>
<p>Une fonction peut avoir pour argument une valeur littérale, une variable ou une fonction. Plusieurs fonctions peuvent ainsi être imbriquées les unes dans les autres, comme des poupées russes :</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="ch">$(</span><span class="dt">fonction</span><span class="er"> </span><span class="ch">$(</span><span class="dt">fonction</span><span class="er"> </span><span class="ch">$(</span><span class="dt">fonction</span><span class="er"> </span><span class="dt">…</span><span class="er"> </span><span class="ch">)))</span></span></code></pre></div>
<p>L’ordre d’exécution est simple à déterminer : c’est la fonction qui ne contient pas elle-même une autre fonction (la plus petite poupée russe) qui s’exécute en premier ; puis elle passe son résultat à la fonction qui la contient, et ainsi de suite jusqu’à la dernière fonction (la plus grande poupée russe), celle qui n’est pas elle-même contenue dans une autre fonction.</p>
<p>La fonction <code>shell</code> est particulièrement utile. Comme son nom l’indique, elle permet de passer des commandes au shell, comme si on les exécutait dans un terminal.</p>
<p>Voir <a href="https://www.gnu.org/software/make/manual/make.html#Functions">Fonctions</a> dans le manuel GNU Make pour la liste complète des fonctions.</p>
</section>
<section id="regles-implicites" class="level1" data-number="6">
<h1 data-number="6"> Règles implicites</h1>
<p>Contrairement aux règles de base (aussi appelées règles explicites), les règles implicites ne définissent pas comment fabriquer une cible précise à partir de prérequis définis, mais comment fabriquer des <em>types de cibles</em> à partir de <em>types de prérequis</em>.</p>
<div class="important">
<p>Une règle implicite ne définit pas de cibles. Lorsqu’on utilise une règle implicite, il faut donc par ailleurs définir explicitement les cibles à fabriquer.</p>
</div>
<section id="modeles-pattern-rules" class="level2" data-number="6.1">
<h2 data-number="6.1"> Modèles <em>(pattern rules)</em></h2>
<p>Dans une règle implicite, au lieu d’indiquer explicitement le nom de cibles et de prérequis, on indique le <strong>modèle</strong> (en anglais <em>pattern</em>) que doivent suivre ces noms. Un modèle de nom contient le symbole pourcentage <code>%</code>, qui constitue la part variable du modèle, avec éventuellement un préfixe et/ou un suffixe, qui constituent la part fixe du modèle.</p>
<p>Exemples :</p>
<ul>
<li><code>%.txt</code> désigne tous les fichiers dont le nom finit par <code>.txt</code></li>
<li><code>_%</code> désigne tous les fichiers dont le nom commence par <code>_</code></li>
<li><code>_%.txt</code> désigne tous les fichiers dont le nom commence par <code>_</code> et finit par <code>.txt</code></li>
</ul>
<div class="exemple">
<p>Dans le Makefile ci-dessous, on génère automatiquement les noms des cibles en utilisant la fonction <code>wildcard</code> et la fonction de référence avec substitution, et on crée une règle implicite qui définit comment fabriquer des fichiers HTML à partir de fichiers Markdown, quel que soit leur nom.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> html</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="co"># liste des noms de fichiers Markdown présents</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="dt">input</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">wildcard</span><span class="st"> *.md</span><span class="ch">)</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a><span class="co"># liste des noms de fichiers HTML à fabriquer</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a><span class="dt">output</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">input</span><span class="kw">:</span><span class="ss">.md</span><span class="kw">=</span><span class="ss">.html</span><span class="ch">)</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a><span class="co"># cible à fabriquer</span></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a><span class="dv">html :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">output</span><span class="ch">)</span></span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a><span class="co"># règle implicite pour la fabrication</span></span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a><span class="dv">%.html :</span><span class="dt"> %.md</span></span>
<span id="cb25-14"><a href="#cb25-14" aria-hidden="true" tabindex="-1"></a> recette …</span></code></pre></div>
</div>
</section>
<section id="variables-automatiques" class="level2" data-number="6.2">
<h2 data-number="6.2"> Variables automatiques</h2>
<p>Comme pour les variables spéciales, les variables automatiques sont des variables dont le nom est réservé. En revanche, leur valeur n’est pas définie manuellement mais calculée automatiquement (d’où leur nom) en fonction du contexte.</p>
<table>
<caption>Exemples de variables automatiques</caption>
<thead>

</thead>
<tbody>
<tr class="odd">
<td><code>$@</code></td>
<td>nom de la cible</td>
</tr>
<tr class="even">
<td><code>$&lt;</code></td>
<td>nom du premier prérequis</td>
</tr>
<tr class="odd">
<td><code>$^</code></td>
<td>noms de tous les prérequis (séparés par des espaces)</td>
</tr>
</tbody>
</table>
<div class="important">
<p>Les variables automatiques ne peuvent être utilisées que dans les recettes.</p>
</div>
<p>Les variables automatiques sont pensées pour être utilisées dans des règles implicites. Combiner les deux permet de réaliser facilement des traitements de fichiers par lots. À mes yeux, c’est l’une des fonctionnalités les plus utiles de Make.</p>
<div class="exemple">
<p>On reprend l’exemple précédent de la règle qui définit comment fabriquer des fichiers HTML à partir de fichiers Markdown et on ajoute une recette qui consiste à exécuter Pandoc en lui donnant le nom des prérequis <code>$^</code> comme entrée et le nom des cibles <code>$@</code> comme sortie.</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> html</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="dt">input</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">wildcard</span><span class="st"> *.md</span><span class="ch">)</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="dt">output</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">input</span><span class="kw">:</span><span class="ss">.md</span><span class="kw">=</span><span class="ss">.html</span><span class="ch">)</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="dv">html :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">output</span><span class="ch">)</span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a><span class="dv">%.html :</span><span class="dt"> %.md</span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a> pandoc <span class="ch">$^</span> --output=<span class="ch">$@</span></span></code></pre></div>
<p>Et voici une légère variation de la recette : on ajoute un prérequis, du coup on remplace <code>$^</code> par <code>$&lt;</code> pour ne plus passer en entrée tous les prérequis mais uniquement le nom du fichier Markdown.</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="dv">%.html :</span><span class="dt"> %.md styles.css</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a> pandoc <span class="ch">$&lt;</span> --css=styles.css --output=<span class="ch">$@</span></span></code></pre></div>
</div>
<p>Voir <a href="https://www.gnu.org/software/make/manual/make.html#Automatic-Variables">Automatic variables</a> dans le manuel GNU Make pour la liste complète des variables automatiques.</p>
</section>
</section>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 381
- 0
cache/2024/821fa933883f080d23c0a6d9d0b3721a/index.md View File

@@ -0,0 +1,381 @@
title: Make
url: https://www.arthurperret.fr/cours/make.html
hash_url: 821fa933883f080d23c0a6d9d0b3721a
archive_date: 2024-03-28
og_image: https://www.arthurperret.fr/img/scriptor.jpg
description: Cette page présente le logiciel Make, un outil simple et fiable pour automatiser des tâches.
favicon: https://www.arthurperret.fr/cours/favicon-32x32.png
language: fr_FR

<h1 data-number="1"> Qu’est-ce que Make ?</h1>
<p><a href="https://www.gnu.org/software/make/">Make</a> fait partie des logiciels d’automatisation des tâches : il sert à créer des processus dans lesquels des actions s’enchaînent automatiquement.</p>
<p>À l’origine, Make est un outil conçu pour le développement de logiciels : il sert à automatiser la fabrication de programmes exécutables à partir de fichiers contenant du code.</p>
<p>Mais en réalité, Make peut être utilisé pour gérer toutes sortes de projets. En tant que langage, il permet de définir des processus de manière très simple (encore plus simple que les <a href="ligne-de-commande.html#scripts-shell">scripts shell</a>). Et en tant qu’outil, il permet de gagner du temps, avec une gestion efficace des états des fichiers qui fait économiser du travail aussi bien à l’humain qu’à la machine.</p>
<section id="origine-gerer-la-fabrication-de-programmes-informatiques" class="level2" data-number="1.1">
<h2 data-number="1.1"> Origine : gérer la fabrication de programmes informatiques</h2>
<p>Make a été inventé pour automatiser la fabrication de programmes exécutables. Un programme exécutable est un fichier qui se fabrique en deux temps : d’abord, on écrit du code dans des fichiers texte ; puis on exécute un programme appelé compilateur qui transforme le code en un fichier exécutable – cette deuxième étape, c’est la compilation.</p>
<p>Make a été conçu en particulier pour déterminer quels fichiers doivent être recompilés lors de la fabrication d’un programme. Lorsqu’un fichier est modifié, il déclenche automatiquement les commandes appropriées. L’objectif était de ne pas tout recompiler si on ne modifiait qu’une petite partie du code d’un programme complexe, afin d’économiser des ressources et gagner du temps.</p>
<p>Pour faire cela, Make s’appuie sur deux choses : la date de dernière modification des fichiers ; et un arbre de dépendances, c’est-à-dire une représentation des relations entre des fichiers cible, qu’on veut fabriquer, et des fichiers source (les « dépendances »), qui sont nécessaires à la fabrication des fichiers cible. Évidemment, un fichier cible peut à son tour être fichier source pour fabriquer un autre fichier cible, et ainsi de suite.</p>
<p>Voici un exemple d’arbre de dépendances :</p>
<figure>
<img src="../img/make-arbre-dependances.svg" alt="Le programme « main.cpp » dépend de quatre fichiers, dont deux dépendent eux-mêmes d’autres fichiers. Diagramme inspiré de makefiletutorial.com">
<figcaption aria-hidden="true">Le programme « main.cpp » dépend de quatre fichiers, dont deux dépendent eux-mêmes d’autres fichiers. Diagramme inspiré de <a href="https://makefiletutorial.com/">makefiletutorial.com</a></figcaption>
</figure>
<p>Si l’une des dépendances change, Make recompile le ou les fichiers cible correspondants. Dans l’exemple ci-dessus, si <code>one.h</code> est modifié, Make recompile <code>one.cpp</code> puis <code>main.cpp</code>, mais pas <code>two.cpp</code>.</p>
</section>
<section id="fonctionnement-les-makefiles" class="level2" data-number="1.2">
<h2 data-number="1.2"> Fonctionnement : les Makefiles</h2>
<p>Le fonctionnement de Make repose sur des fichiers texte appelés <em>Makefiles</em>. Dans un Makefile, on rédige des instructions appelées <strong>règles</strong> (<em>rules</em>) ; chaque règle définit une <strong>recette</strong> (<em>recipe</em>) pour fabriquer une ou plusieurs <strong>cibles</strong> (<em>targets</em>) suivant des <strong>prérequis</strong> (<em>prerequisites</em>).</p>
<p>Les règles s’écrivent de la manière suivante :</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dv">cibles :</span><span class="dt"> prérequis</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> recette</span></code></pre></div>
<p>Ce sont les relations entre cibles et prérequis qui constituent l’arbre de dépendances modélisé par Make.</p>
<p>Une recette est constituée d’une ou plusieurs <strong>commandes</strong> : des instructions textuelles qu’on exécuterait habituellement dans un terminal.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dv">cibles :</span><span class="dt"> prérequis</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> commande</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> commande</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> commande</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> …</span></code></pre></div>
<p>Comme dans de nombreux langages informatiques, la syntaxe des Makefiles inclut la possibilité de créer des variables, de laisser des commentaires, de définir des fonctions qui incluent des étapes logiques, etc.</p>
<p>Pour exécuter les instructions contenues dans un Makefile, il faut ouvrir un terminal, se déplacer à l’emplacement du Makefile, puis exécuter la commande <code>make</code> (ce qui déclenche les instructions qui sont contenues dans le Makefile).</p>
<p>Si vous souhaitez découvrir comment utiliser un terminal, consultez ma page <a href="ligne-de-commande.html" class="cours">Ligne de commande</a>.</p>
</section>
<section id="utilisation-en-dehors-du-developpement-logiciel" class="level2" data-number="1.3">
<h2 data-number="1.3"> Utilisation en dehors du développement logiciel</h2>
<p>Progressivement, d’autres usages de Make ont émergé. En effet, son fonctionnement a une portée plus générale que la seule fabrication de programmes, car il ne pose aucune restriction sur la nature de la cible, des prérequis ou de la recette. On peut utiliser Make pour automatiser le déclenchement de n’importe quel programme qui s’utilise habituellement à la ligne de commande.</p>
<p>Donc si Make est souvent présenté comme un outil d’automatisation pour la <em>compilation de programmes</em>, il serait en fait plus juste de le décrire comme un outil d’automatisation pour la <em>ligne de commande</em>. C’est un outil qui permet de définir des enchaînements de commandes textuelles ; dit de façon plus abstraite, Make permet de créer des processus, des séquences d’actions, où chaque action est une instruction envoyée à un ou plusieurs programmes à interface textuelle. Bref : Make permet de fabriquer des fabriques. (Clin d’œil à Antoine Fauchié qui a beaucoup travaillé sur ce concept de fabrique, voir <a href="https://www.quaternum.net/2023/06/02/fabrique-concept/">son blog</a> et <a href="https://these.quaternum.net/concepts/#fabrique">sa thèse</a>.)</p>
<div class="exemple">
<p>J’ai partagé un modèle de Makefile qui automatise l’utilisation de <a href="pandoc.html">Pandoc</a> pour générer différents exports à partir d’une même source : <a href="https://github.com/infologie/pandoc-ssp">Pandoc-SSP</a>. Make a complètement remplacé mon usage des scripts shell pour automatiser le déclenchement de commandes Pandoc. J’utiliserai Pandoc comme exemple à plusieurs reprises sur cette page.</p>
<p>Make me sert donc à rédiger des recettes dans lesquelles je passe d’ingrédients de base (des fichiers) à un résultat final (d’autres fichiers). Mais comme me l’a fait remarquer David Larlet, on peut aussi l’utiliser pour des processus qui n’ont pas forcément de fichiers en entrée ou en sortie, comme la mise en place d’un serveur ou bien l’installation d’un logiciel. Make est un outil versatile.</p>
</div>
</section>
<section id="outils-requis-pour-utiliser-make" class="level2" data-number="1.4">
<h2 data-number="1.4"> Outils requis pour utiliser Make</h2>
<p>Pour utiliser Make, vous avez besoin des choses suivantes :</p>
<dl>
<dt>Un terminal</dt>
<dd>
Si vous n’êtes pas familier de ce type d’environnement, consultez ma page <a href="ligne-de-commande.html" class="cours">Ligne de commande</a>.
</dd>
<dt>Le programme Make</dt>
<dd>
Make existe dans plusieurs versions. Cette page est écrite en référence à <a href="https://www.gnu.org/software/make/manual/">GNU Make</a>, la version la plus répandue. Sur Linux et macOS, Make est pré-installé ; pour vérifier de quelle version vous disposez, exécutez <code>make --version</code> dans un terminal. Sur Windows, vous pouvez installer Make par exemple en installant le gestionnaire de programmes <a href="https://chocolatey.org/install">Chocolatey</a>, puis en utilisant Chocolatey pour installer Make en ouvrant un terminal (je recommande PowerShell) et en exécutant la commande <code>choco install make</code>.
</dd>
<dt>Un éditeur de texte</dt>
<dd>
Pour rédiger des Makefiles. Je recommande souvent <a href="https://notepad-plus-plus.org">Notepad++</a> (Windows), <a href="http://www.barebones.com/products/bbedit/index.html">BBEdit</a> (macOS) et <a href="https://doc.ubuntu-fr.org/gedit">gedit</a> (Linux), mais n’importe quel éditeur de texte fera l’affaire.
</dd>
</dl>
</section>
</section>
<section id="prise-en-main-ecrire-un-makefile-et-executer-make" class="level1" data-number="2">
<h1 data-number="2"> Prise en main : écrire un Makefile et exécuter Make</h1>
<p>Make s’utilise en écrivant des Makefiles et en utilisant la commande <code>make</code> dans un terminal. Par défaut, <code>make</code> cherche un fichier appelé <code>makefile</code> ou <code>Makefile</code> situé dans le répertoire courant. On peut aussi nommer un Makefile par n’importe quel <code>nom</code> et utiliser <code>make</code> avec l’option <code>--file=nom</code> ou <code>-f nom</code>.</p>
<section id="regles-cibles-prerequis-recettes" class="level2" data-number="2.1">
<h2 data-number="2.1"> Règles : cibles, prérequis, recettes</h2>
<p>Un Makefile contient une ou plusieurs règles.</p>
<p>La syntaxe générale d’une règle est la suivante :</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dv">cibles :</span><span class="dt"> prérequis</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> recette</span></code></pre></div>
<div class="important">
<p>Notez l’espacement avant le mot « recette » : dans une règle, <strong>la recette doit être indentée avec une tabulation</strong> (une seule tabulation, pas d’espaces).</p>
<p>(Si comme moi vous utilisez régulièrement YAML, un langage qui requiert d’indenter les lignes avec des espaces, préparez-vous à vous mélanger les pinceaux pendant quelques temps !)</p>
</div>
<p>Les <strong>cibles</strong> et les <strong>prérequis</strong> sont des noms de fichiers, séparés par des espaces. En pratique, il n’y a souvent qu’une cible par règle.</p>
<p>La <strong>recette</strong> est constituée d’une ou plusieurs <strong>commandes</strong>, qui sont une série d’étapes utilisées pour créer la cible. Make applique la recette :</p>
<ul>
<li>si les cibles n’existent pas ;</li>
<li>si les cibles existent mais que les prérequis ont une date de dernière modification plus récente que les cibles.</li>
</ul>
<div class="exemple">
<p>Dans le Makefile suivant, la cible est un fichier PDF, <code>doc.pdf</code>. Elle a un prérequis qui est un fichier texte rédigé en Markdown, <code>doc.md</code>. La recette est constituée par une commande qui exécute le programme Pandoc.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dv">doc.pdf :</span><span class="dt"> doc.md</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> pandoc doc.md -o doc.pdf</span></code></pre></div>
<p>Si j’exécute <code>make</code> :</p>
<ul>
<li>Si <code>doc.pdf</code> n’existe pas, ou qu’il existe mais que <code>doc.md</code> est plus récent (donc qu’il a été modifié depuis la dernière fabrication de <code>doc.pdf</code>), Make lance la commande Pandoc qui convertit le fichier Markdown en PDF.</li>
<li>Si <code>doc.pdf</code> existe et que <code>doc.md</code> n’est pas plus récent, rien ne se passe.</li>
</ul>
</div>
</section>
<section id="cibles-factices" class="level2" data-number="2.2">
<h2 data-number="2.2"> Cibles factices</h2>
<p>Les cibles peuvent être des cibles « factices » (en anglais <em>phony</em>). Une cible factice ne correspond pas à un fichier réel : c’est une sorte de nom de code pour une série d’opérations qu’on veut exécuter. Les cibles factices doivent être déclarées comme prérequis de la cible spéciale <code>.PHONY</code>.</p>
<div class="exemple">
<p>Je reprends l’exemple précédent, avec cette fois deux fichiers Markdown, <code>doc-fr.md</code> et <code>doc-en.md</code>. Je définis une cible <code>all</code> qui a deux prérequis, les cibles <code>doc-fr.pdf</code> et <code>doc-en.pdf</code>.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-fr.pdf :</span><span class="dt"> doc-fr.md</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> pandoc doc-fr.md -o doc-fr.pdf</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-en.pdf :</span><span class="dt"> doc-en.md</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> pandoc doc-en.md -o doc-en.pdf</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span></code></pre></div>
<p>Si j’exécute <code>make all</code>, Make déclenchera la fabrication des deux cibles correspondantes, à moins que les cibles existent déjà et qu’elles soient plus récentes que les prérequis.</p>
</div>
</section>

<section id="ordre-dexecution-des-regles" class="level2" data-number="2.4">
<h2 data-number="2.4"> Ordre d’exécution des règles</h2>
<p>Si un Makefile contient plusieurs règles, par défaut <code>make</code> fabrique la première cible qu’il trouve dans le fichier. Il est possible de définir manuellement la cible à exécuter par défaut grâce à la variable spéciale <code>.DEFAULT_GOAL</code> (voir la section <a href="#variables-speciales">Variables spéciales</a> plus bas).</p>
</section>
<section id="options-dexecution" class="level2" data-number="2.5">
<h2 data-number="2.5"> Options d’exécution</h2>
<p>La commande <code>make</code> dispose de nombreuses options qui modifient son comportement. Utilisez <code>make --option</code> pour appliquer l’option correspondante lors de l’utilisation de Make. Plusieurs options peuvent être utilisées simultanément. La plupart des options sont disponibles sous plusieurs noms (notamment pour des questions de compatibilité), dont des formes raccourcies avec un seul tiret et une seule lettre, pour aller plus vite lorsqu’on saisit les commandes manuellement.</p>
<p>Quelques exemples :</p>
<ul>
<li><code>-n</code> (ou <code>--just-print</code>, <code>--dry-run</code>, <code>--recon</code>) : affiche les commandes qui seraient exécutées, sans les exécuter. Ceci permet de vérifier ce que ferait un Makefile sans l’appliquer réellement.</li>
<li><code>-j [jobs]</code> (ou <code>--jobs[=jobs]</code>) : autorise Make à exécuter simultanément un certain nombre de commandes (<code>jobs</code>). Si votre machine dispose d’un processeurs avec plusieurs cœurs, ceci permet de réduire considérablement le temps d’exécution du Makefile. Par exemple, avec un processeur à huit cœurs, vous pouvez utiliser l’option <code>-j 8</code> pour que Make lance jusqu’à huit commandes en parallèle (une par cœur).</li>
</ul>
<p>Dans un terminal, utilisez <code>man make</code> pour afficher le manuel et découvrir les options. Appuyez sur la touche <kbd>q</kbd> pour quitter le manuel.</p>
</section>
<section id="supprimer-lecho" class="level2" data-number="2.6">
<h2 data-number="2.6"> Supprimer l’écho</h2>
<p>Par défaut, Make affiche dans le terminal chaque ligne d’une recette exécutée. On appelle cela « l’écho », par analogie avec la commande <code>echo</code> qu’on utilise dans un terminal pour afficher des messages.</p>
<p>Il y a plusieurs manières de supprimer l’écho :</p>
<ul>
<li>On peut préfixer une ligne par une arobase <code>@</code> pour supprimer l’écho pour cette ligne.</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb7"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-fr.pdf :</span><span class="dt"> doc-fr.md</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="ch">@</span><span class="fu">pandoc doc-fr.md -o doc-fr.pdf</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-en.pdf :</span><span class="dt"> doc-en.md</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="ch">@</span><span class="fu">pandoc doc-en.md -o doc-en.pdf</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span></code></pre></div>
</div>
<p>Remarque : il existe d’autres préfixes qu’on peut ajouter à une ligne pour modifier le comportement de Make. Par exemple, préfixer une ligne par <code>-</code> indique à Make qu’il ne doit pas s’interrompre en cas d’erreur, ce qui est pratique lorsqu’on dépanne un processus.</p>
<ul>
<li>On peut utiliser la cible spéciale <code>.SILENT</code> en indiquant des prérequis, ce qui supprimera l’écho pour la fabrication des prérequis correspondants.</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb8"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="ot">.SILENT:</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-fr.pdf :</span><span class="dt"> doc-fr.md</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> pandoc doc-fr.md -o doc-fr.pdf</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="dv">doc-en.pdf :</span><span class="dt"> doc-en.md</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> pandoc doc-en.md -o doc-en.pdf</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc-fr.pdf doc-en.pdf</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="co"># "make all" sera silencieux aussi !</span></span></code></pre></div>
</div>
<p>Remarque : <code>.PHONY</code> et <code>.SILENT</code> sont deux exemples de cibles spéciales. Voir <a href="https://www.gnu.org/software/make/manual/make.html#Special-Targets">Special Targets</a> dans le manuel GNU Make pour la liste complète.</p>
<ul>
<li>On peut utiliser l’option <code>-s</code> (ou <code>--silent</code>, <code>--quiet</code>) au moment d’exécuter la commande <code>make</code>. Toutes les recettes s’exécuteront alors silencieusement.</li>
</ul>
</section>
</section>
<section id="variables" class="level1" data-number="3">
<h1 data-number="3"> Variables</h1>
<p>Make permet de créer des variables, c’est-à-dire d’affecter une valeur à un nom pour pouvoir ensuite utiliser le nom à la place de la valeur. L’intérêt premier des variables, c’est d’éviter d’écrire plusieurs fois la même chose, pour économiser du travail et réduire le risque de faire des erreurs.</p>
<p>Pour définir une variable :</p>

<p>Pour utiliser la valeur de cette variable :</p>

<div class="exemple">
<p>Dans le Makefile ci-dessous, une longue liste de fichiers apparaît plusieurs fois :</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="dv">edit :</span><span class="dt"> main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="dv">clean :</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span></code></pre></div>
<p>On peut définir une variable dont la valeur est cette liste de fichiers, puis utiliser le nom de la variable partout où cette liste doit être utilisée :</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="dt">objects</span> <span class="ch">=</span><span class="st"> main.o kbd.o command.o display.o insert.o search.o files.o utils.o</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="dv">edit :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">objects</span><span class="ch">)</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> cc -o edit <span class="ch">$(</span><span class="dt">objects</span><span class="ch">)</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="dv">clean :</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> rm edit <span class="ch">$(</span><span class="dt">objects</span><span class="ch">)</span></span></code></pre></div>
</div>
<p>Une variable peut être redéfinie, c’est-à-dire qu’on peut la définir à nouveau dans le même Makefile. La nouvelle valeur remplace alors la précédente.</p>
<div class="exemple">
<div class="sourceCode" id="cb13"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> Bonjour !</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> Bonsoir !</span></span></code></pre></div>
</div>
<p>La valeur d’une variable peut elle-même contenir une ou plusieurs variables.</p>
<div class="exemple">
<div class="sourceCode" id="cb14"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">txt</span> <span class="ch">=</span><span class="st"> doc1.txt doc2.txt</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="dt">img</span> <span class="ch">=</span><span class="st"> soleil.svg lune.svg</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="dt">fichiers</span><span class="ch"> =</span><span class="st"> </span><span class="ch">$(</span><span class="dt">txt</span><span class="ch">)</span><span class="st"> </span><span class="ch">$(</span><span class="dt">img</span><span class="ch">)</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "fichiers" est "doc1.txt doc2.txt soleil.svg lune.svg"</span></span></code></pre></div>
</div>
<div class="important">
<p>La plupart des langages informatiques reconnaissent plusieurs types de données, soit de manière implicite, soit de manière explicite grâce à l’ajout de caractères spéciaux. Exemple en JavaScript :</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> <span class="op">=</span> <span class="kw">true</span> <span class="co">// booléen (boolean)</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> <span class="op">=</span> <span class="dv">28</span> <span class="co">// nombre entier (integer)</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> <span class="op">=</span> <span class="st">"Bonjour !"</span> <span class="co">// chaîne de caractères (string)</span></span></code></pre></div>
<p><strong>Make ne reconnaît qu’un type de données : la chaîne de caractères</strong> (en anglais : <em>string</em>). Par conséquent, il interprète les guillemets littéralement. Il est donc conseillé de n’utiliser les guillemets que lorsque la chaîne va être interprétée dans un autre environnement, par exemple lorsqu’on passe une variable au shell pour exécuter une commande.</p>
<p>Ainsi, plutôt que :</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> "Bonjour !"</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="dv">test :</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> echo <span class="ch">$(</span><span class="dt">message</span><span class="ch">)</span></span></code></pre></div>
<p>On écrira :</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="dt">message</span> <span class="ch">=</span><span class="st"> Bonjour !</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="dv">test :</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> echo <span class="st">"</span><span class="ch">$(</span><span class="dt">message</span><span class="ch">)</span><span class="st">"</span></span></code></pre></div>
</div>
<section id="expansion-et-operateurs-de-definition" class="level2" data-number="3.1">
<h2 data-number="3.1"> Expansion et opérateurs de définition</h2>
<p>On parle d’expansion pour désigner le fait de remplacer une expression de la forme <code>$(nom)</code> par la valeur de la variable correspondante.</p>
<p>Ce concept devient important lorsqu’on définit des variables qui contiennent d’autres variables, et plus encore lorsque des variables sont redéfinies.</p>
<p>En effet, Make permet de gérer de manière très fine l’ordre dans lequel se passe l’expansion. Ceci repose sur l’utilisation de différents opérateurs de définition (voir la section <a href="https://www.gnu.org/software/make/manual/make.html#Reading-Makefiles">How Make reads a Makefile</a> du manuel de GNU Make pour la liste des opérateurs et leurs effets). Or les différences de comportement entre ces opérateurs sont souvent source de confusion.</p>
<p>Les deux opérateurs qu’on rencontre le plus fréquemment sont <code>=</code> et <code>:=</code>.</p>
<ul>
<li>Quand on utilise <code>=</code> pour définir une variable, l’expansion des variables contenues dans la valeur se fait au moment où la variable est <strong>utilisée</strong>. Ceci permet de changer la valeur en cours de route, si on redéfinit les variables utilisées dans la valeur.</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb18"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonjour</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="dt">hello</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">mot</span><span class="ch">)</span><span class="st"> à vous !</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est "bonjour à vous !"</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="co"># on redéfinit "mot" en changeant sa valeur</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonsoir</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est maintenant "bonsoir à vous !"</span></span></code></pre></div>
</div>
<ul>
<li>Quand on utilise <code>:=</code> pour définir une variable, l’expansion des variables contenues dans la valeur se fait au moment où la variable est <strong>définie</strong>. Ceci permet de fixer la valeur une fois pour toutes. Exemple :</li>
</ul>
<div class="exemple">
<div class="sourceCode" id="cb19"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonjour</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="dt">hello</span><span class="ch"> :=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">mot</span><span class="ch">)</span><span class="st"> à vous !</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est "bonjour à vous !"</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="co"># on redéfinit "mot"</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="dt">mot</span> <span class="ch">=</span><span class="st"> bonsoir</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" reste "bonjour à vous !"</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a><span class="co"># on redéfinit cette fois "hello", on répète la première définition</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="dt">hello</span><span class="ch"> :=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">mot</span><span class="ch">)</span><span class="st"> à vous !</span></span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a><span class="co"># la valeur de "hello" est maintenant "bonsoir à vous !"</span></span></code></pre></div>
</div>
<p>En résumé :</p>
<ul>
<li>Si on définit des variables qui contiennent des variables, et qu’on modifie ces définitions en cours de route, alors on peut alterner entre <code>=</code> et <code>:=</code> suivant les besoins.</li>
<li>En revanche, lorsqu’une variable contient une valeur simple (on parle aussi de valeur littérale), c’est-à-dire qu’elle ne contient pas elle-même de variable, alors utiliser <code>=</code> ou <code>:=</code> ne fait <strong>aucune différence</strong>, puisqu’il n’y a rien dans la valeur de la variable définie qui nécessite une expansion.</li>
<li>Et lorsque le Makefile ne contient que des variables définies une seule fois, alors utiliser <code>=</code> ou <code>:=</code> ne fait <strong>aucune différence non plus</strong>, puisqu’il n’y a aucune redéfinition qui vient affecter l’expansion.</li>
</ul>
</section>
<section id="variables-speciales" class="level2" data-number="3.2">
<h2 data-number="3.2"> Variables spéciales</h2>
<p>Make inclut quelques variables <em>spéciales</em>, dont le nom est réservé pour un comportement prédéfini.</p>
<p>Par exemple, lorsqu’on exécute <code>make</code> sans argument, c’est la première règle contenue dans le Makefile qui est appliquée. On peut utiliser la variable spéciale <code>.DEFAULT_GOAL</code> pour définir la règle à appliquer par défaut.</p>
<div class="exemple">
<p>Avec le Makefile ci-dessous, exécuter <code>make</code> fabriquera <code>doc.html</code> au lieu de <code>all</code> :</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> all</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="dt">.DEFAULT_GOAL</span><span class="ch"> :=</span><span class="st"> doc.html</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a><span class="dv">all :</span><span class="dt"> doc.pdf doc.html</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a><span class="dv">doc.pdf :</span><span class="dt"> doc.md</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a> pandoc doc.md -o doc.pdf</span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a><span class="dv">doc.html :</span><span class="dt"> doc.md</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a> pandoc doc.md -o doc.html</span></code></pre></div>
</div>
<p>Voir <a href="https://www.gnu.org/software/make/manual/make.html#Special-Variables">Special Variables</a> dans le manuel GNU Make pour la liste complète.</p>
</section>
</section>
<section id="fonctions" class="level1" data-number="5">
<h1 data-number="5"> Fonctions</h1>
<p>Make inclut la possibilité d’utiliser des fonctions pour manipuler du texte. Il y a des fonctions prédéfinies qui correspondent aux opérations les plus courantes : trier, chercher, filtrer, tronquer, etc. Il est également possible de définir ses propres fonctions.</p>
<p>Une fonction s’utilise en faisant appel à son nom et en lui passant du texte en argument :</p>

<div class="exemple">
<ul>
<li>La fonction <code>$(wildcard pattern)</code> cherche les noms de fichiers qui correspondent au motif <code>pattern</code>.</li>
<li>La fonction de référence avec substitution <code>$(var:a=b)</code> remplace la chaîne de caractères <code>a</code> par <code>b</code> dans la variable <code>var</code>.</li>
</ul>
<p>Dans l’exemple ci-dessous, on combine ces deux fonctions pour créer une variable <code>input</code> qui contient les noms de fichiers Markdown présents dans le répertoire où se situe le Makefile, puis une variable <code>output</code> qui contient les mêmes noms en remplaçant l’extension <code>.md</code> par <code>.html</code>.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="dt">input</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">wildcard</span><span class="st"> *.md</span><span class="ch">)</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="dt">output</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">input</span><span class="kw">:</span><span class="ss">.md</span><span class="kw">=</span><span class="ss">.html</span><span class="ch">)</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="co"># si input a pour valeur par exemple "doc.md 2024-03-25.md"</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a><span class="co"># output aura pour valeur "doc.html 2024-03-25.html"</span></span></code></pre></div>
</div>
<p>Une fonction peut avoir pour argument une valeur littérale, une variable ou une fonction. Plusieurs fonctions peuvent ainsi être imbriquées les unes dans les autres, comme des poupées russes :</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="ch">$(</span><span class="dt">fonction</span><span class="er"> </span><span class="ch">$(</span><span class="dt">fonction</span><span class="er"> </span><span class="ch">$(</span><span class="dt">fonction</span><span class="er"> </span><span class="dt">…</span><span class="er"> </span><span class="ch">)))</span></span></code></pre></div>
<p>L’ordre d’exécution est simple à déterminer : c’est la fonction qui ne contient pas elle-même une autre fonction (la plus petite poupée russe) qui s’exécute en premier ; puis elle passe son résultat à la fonction qui la contient, et ainsi de suite jusqu’à la dernière fonction (la plus grande poupée russe), celle qui n’est pas elle-même contenue dans une autre fonction.</p>
<p>La fonction <code>shell</code> est particulièrement utile. Comme son nom l’indique, elle permet de passer des commandes au shell, comme si on les exécutait dans un terminal.</p>
<p>Voir <a href="https://www.gnu.org/software/make/manual/make.html#Functions">Fonctions</a> dans le manuel GNU Make pour la liste complète des fonctions.</p>
</section>
<section id="regles-implicites" class="level1" data-number="6">
<h1 data-number="6"> Règles implicites</h1>
<p>Contrairement aux règles de base (aussi appelées règles explicites), les règles implicites ne définissent pas comment fabriquer une cible précise à partir de prérequis définis, mais comment fabriquer des <em>types de cibles</em> à partir de <em>types de prérequis</em>.</p>
<div class="important">
<p>Une règle implicite ne définit pas de cibles. Lorsqu’on utilise une règle implicite, il faut donc par ailleurs définir explicitement les cibles à fabriquer.</p>
</div>
<section id="modeles-pattern-rules" class="level2" data-number="6.1">
<h2 data-number="6.1"> Modèles <em>(pattern rules)</em></h2>
<p>Dans une règle implicite, au lieu d’indiquer explicitement le nom de cibles et de prérequis, on indique le <strong>modèle</strong> (en anglais <em>pattern</em>) que doivent suivre ces noms. Un modèle de nom contient le symbole pourcentage <code>%</code>, qui constitue la part variable du modèle, avec éventuellement un préfixe et/ou un suffixe, qui constituent la part fixe du modèle.</p>
<p>Exemples :</p>
<ul>
<li><code>%.txt</code> désigne tous les fichiers dont le nom finit par <code>.txt</code></li>
<li><code>_%</code> désigne tous les fichiers dont le nom commence par <code>_</code></li>
<li><code>_%.txt</code> désigne tous les fichiers dont le nom commence par <code>_</code> et finit par <code>.txt</code></li>
</ul>
<div class="exemple">
<p>Dans le Makefile ci-dessous, on génère automatiquement les noms des cibles en utilisant la fonction <code>wildcard</code> et la fonction de référence avec substitution, et on crée une règle implicite qui définit comment fabriquer des fichiers HTML à partir de fichiers Markdown, quel que soit leur nom.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> html</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="co"># liste des noms de fichiers Markdown présents</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="dt">input</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">wildcard</span><span class="st"> *.md</span><span class="ch">)</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a><span class="co"># liste des noms de fichiers HTML à fabriquer</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a><span class="dt">output</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">input</span><span class="kw">:</span><span class="ss">.md</span><span class="kw">=</span><span class="ss">.html</span><span class="ch">)</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a><span class="co"># cible à fabriquer</span></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a><span class="dv">html :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">output</span><span class="ch">)</span></span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a><span class="co"># règle implicite pour la fabrication</span></span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a><span class="dv">%.html :</span><span class="dt"> %.md</span></span>
<span id="cb25-14"><a href="#cb25-14" aria-hidden="true" tabindex="-1"></a> recette …</span></code></pre></div>
</div>
</section>
<section id="variables-automatiques" class="level2" data-number="6.2">
<h2 data-number="6.2"> Variables automatiques</h2>
<p>Comme pour les variables spéciales, les variables automatiques sont des variables dont le nom est réservé. En revanche, leur valeur n’est pas définie manuellement mais calculée automatiquement (d’où leur nom) en fonction du contexte.</p>
<table>
<caption>Exemples de variables automatiques</caption>
<thead>

</thead>
<tbody>
<tr class="odd">
<td><code>$@</code></td>
<td>nom de la cible</td>
</tr>
<tr class="even">
<td><code>$&lt;</code></td>
<td>nom du premier prérequis</td>
</tr>
<tr class="odd">
<td><code>$^</code></td>
<td>noms de tous les prérequis (séparés par des espaces)</td>
</tr>
</tbody>
</table>
<div class="important">
<p>Les variables automatiques ne peuvent être utilisées que dans les recettes.</p>
</div>
<p>Les variables automatiques sont pensées pour être utilisées dans des règles implicites. Combiner les deux permet de réaliser facilement des traitements de fichiers par lots. À mes yeux, c’est l’une des fonctionnalités les plus utiles de Make.</p>
<div class="exemple">
<p>On reprend l’exemple précédent de la règle qui définit comment fabriquer des fichiers HTML à partir de fichiers Markdown et on ajoute une recette qui consiste à exécuter Pandoc en lui donnant le nom des prérequis <code>$^</code> comme entrée et le nom des cibles <code>$@</code> comme sortie.</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> html</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="dt">input</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">wildcard</span><span class="st"> *.md</span><span class="ch">)</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="dt">output</span> <span class="ch">=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">input</span><span class="kw">:</span><span class="ss">.md</span><span class="kw">=</span><span class="ss">.html</span><span class="ch">)</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="dv">html :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">output</span><span class="ch">)</span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a><span class="dv">%.html :</span><span class="dt"> %.md</span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a> pandoc <span class="ch">$^</span> --output=<span class="ch">$@</span></span></code></pre></div>
<p>Et voici une légère variation de la recette : on ajoute un prérequis, du coup on remplace <code>$^</code> par <code>$&lt;</code> pour ne plus passer en entrée tous les prérequis mais uniquement le nom du fichier Markdown.</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="dv">%.html :</span><span class="dt"> %.md styles.css</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a> pandoc <span class="ch">$&lt;</span> --css=styles.css --output=<span class="ch">$@</span></span></code></pre></div>
</div>
<p>Voir <a href="https://www.gnu.org/software/make/manual/make.html#Automatic-Variables">Automatic variables</a> dans le manuel GNU Make pour la liste complète des variables automatiques.</p>
</section>
</section>

+ 197
- 0
cache/2024/a11451e038f3aedba0ae55ff242e1ed5/index.html View File

@@ -0,0 +1,197 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>“I’m not a cynic, I’m disappointed” (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://www.baldurbjarnason.com/2024/the-software-crisis-easter-sale/">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>“I’m not a cynic, I’m disappointed”</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://www.baldurbjarnason.com/2024/the-software-crisis-easter-sale/" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-28
</p>
</nav>
<hr>
<p>Most people don’t realise just how few of the “critics” in tech are genuine cynics.</p>
<p>You don’t spend a good part of your life shouting about bad websites or broken software and how they could be fixed if you’re a cynic.</p>
<p>Cynics don’t believe things can be fixed and they don’t believe that fixing things would help in the first place.</p>
<p>Somebody who is constantly pointing out various instances of software inaccessibility isn’t doing so because they’re a cynic. They believe this can be done better; they were optimistic enough to expect more; and now they’re disappointed.</p>
<p>And anybody who thinks that pointing out common flaws in the tech industry is good for your career is <em>hilariously</em> wrong. <em>Dramatically pulls out his wallet using only his index finger and thumb. Carefully opens it. A cartoonish moth flies out of the empty wallet.</em></p>
<p>People who point out what needs to be improved are generally disappointed optimists. Only an optimist would believe that pointing out what has gone wrong could ever result in said issue being fixed. Only somebody who believes that software could be universally useful to everybody in society is going to spend time discovering and highlighting accessibility issues.</p>
<p>You don’t spend years of your life figuring out how the flaws of the web can be fixed unless you thinks the web has massive unfulfilled potential.</p>
<p>We’re not cynics. If we were, we wouldn’t waste so much energy being hopeful.</p>
<p>But, people in tech frequently seem to believe that people like me are haters – that we’re out to <em>prevent</em> the industry from doing amazing things.</p>
<p>That, the seeming slow progress, and the frequent setbacks get tiring after a while. We’re only a quarter of the way through the year and I’m already exhausted.</p>
<p>I’m not a religious person, but Easter is coming around right at a time when I need a bit of a break, so a break it is.</p>
<p>However, being the eternal optimist that I am, I figure I might has well run an Easter sale while I’m off on the couch watching movies.</p>
<p>So, until the end of day 1 April, the discount code <code>EASTER24</code> will give you a <strong>$10 USD</strong> off any of my ebooks. That means that you can get <em>Out of the Software Crisis</em> and <em>The Intelligence Illusion</em> for <strong>$25 USD</strong> each.</p>
<p>And, yes, it means that, since their normal price starts at $10 USD you can get the ebook version of Yellow or my essay collection <em>Bad Writing And Other Essays</em> for <strong><em>$0 USD</em></strong>.</p>
<p>(Though, in the case of <em>Bad Writing</em> you have the option of paying more if you want to support this blog.)</p>
<p>The discount code again:</p>
<p><code>EASTER24</code></p>
<p>The ebooks:</p>

<p>In the meantime, try to be kind to yourselves and forgiving of your own flaws. Not because of some religious thing.</p>
<p><em>Do it because it makes sense.</em></p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 33
- 0
cache/2024/a11451e038f3aedba0ae55ff242e1ed5/index.md View File

@@ -0,0 +1,33 @@
title: “I’m not a cynic, I’m disappointed”
url: https://www.baldurbjarnason.com/2024/the-software-crisis-easter-sale/
hash_url: a11451e038f3aedba0ae55ff242e1ed5
archive_date: 2024-03-28
og_image: https://www.baldurbjarnason.com/favicon-96x96.png
description: Most people don’t realise just how few of the “critics” in tech are genuine cynics.You don’t spend a good part of your life shouting about bad websites or broken software and how they could be fixed if you’re a cynic.
favicon: https://www.baldurbjarnason.com/dark-bird.svg
language: en_US

<p>Most people don’t realise just how few of the “critics” in tech are genuine cynics.</p>
<p>You don’t spend a good part of your life shouting about bad websites or broken software and how they could be fixed if you’re a cynic.</p>
<p>Cynics don’t believe things can be fixed and they don’t believe that fixing things would help in the first place.</p>
<p>Somebody who is constantly pointing out various instances of software inaccessibility isn’t doing so because they’re a cynic. They believe this can be done better; they were optimistic enough to expect more; and now they’re disappointed.</p>
<p>And anybody who thinks that pointing out common flaws in the tech industry is good for your career is <em>hilariously</em> wrong. <em>Dramatically pulls out his wallet using only his index finger and thumb. Carefully opens it. A cartoonish moth flies out of the empty wallet.</em></p>
<p>People who point out what needs to be improved are generally disappointed optimists. Only an optimist would believe that pointing out what has gone wrong could ever result in said issue being fixed. Only somebody who believes that software could be universally useful to everybody in society is going to spend time discovering and highlighting accessibility issues.</p>
<p>You don’t spend years of your life figuring out how the flaws of the web can be fixed unless you thinks the web has massive unfulfilled potential.</p>
<p>We’re not cynics. If we were, we wouldn’t waste so much energy being hopeful.</p>
<p>But, people in tech frequently seem to believe that people like me are haters – that we’re out to <em>prevent</em> the industry from doing amazing things.</p>
<p>That, the seeming slow progress, and the frequent setbacks get tiring after a while. We’re only a quarter of the way through the year and I’m already exhausted.</p>
<p>I’m not a religious person, but Easter is coming around right at a time when I need a bit of a break, so a break it is.</p>
<p>However, being the eternal optimist that I am, I figure I might has well run an Easter sale while I’m off on the couch watching movies.</p>
<p>So, until the end of day 1 April, the discount code <code>EASTER24</code> will give you a <strong>$10 USD</strong> off any of my ebooks. That means that you can get <em>Out of the Software Crisis</em> and <em>The Intelligence Illusion</em> for <strong>$25 USD</strong> each.</p>
<p>And, yes, it means that, since their normal price starts at $10 USD you can get the ebook version of Yellow or my essay collection <em>Bad Writing And Other Essays</em> for <strong><em>$0 USD</em></strong>.</p>
<p>(Though, in the case of <em>Bad Writing</em> you have the option of paying more if you want to support this blog.)</p>
<p>The discount code again:</p>
<p><code>EASTER24</code></p>
<p>The ebooks:</p>




<p>In the meantime, try to be kind to yourselves and forgiving of your own flaws. Not because of some religious thing.</p>
<p><em>Do it because it makes sense.</em></p>

+ 180
- 0
cache/2024/c2a852eced710f481135a1f61cb67a26/index.html View File

@@ -0,0 +1,180 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>Why should a company? · Applied Cartography (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://jmduke.com/posts/microblog/why-should-a-company/">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>Why should a company? · Applied Cartography</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://jmduke.com/posts/microblog/why-should-a-company/" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-28
</p>
</nav>
<hr>
<p>For a long time, my goal with Buttondown was largely around failure avoidance: "I want to get my first paying customer so I know it's not a fake product"; "I want to hit a thousand dollars in revenue so I know it's not just friends humoring me"; "I want to make $10K/mo or else I won't be able to work on it full-time if I want to"; and so on. It's now at the point where those kinds of milestones don't really exist: it's profitable enough to support my salary and the salary of others, it grows every month.</p>
<p>As such, I've become more attuned to what my goal really <em>should be</em> with Buttondown. Most tech companies have a goal centered around some flavor of maximalism: disrupting an industry, revolutionizing a process, that kind of thing. Buttondown is not that kind of product; I am not that kind of person.</p>
<p>And yet I <em>do</em> want Buttondown to grow larger than it currently is: not by leaps and bounds, of course, but there are things that I think <em>make it a better product</em> that require further investment and I genuinely think there are a lot of people out there who would be happier using Buttondown than using their current tool. Both of those things imply a size of company that is <em>larger</em> than Buttondown's current size. (I'm also having a lot of conversations with folks considering striking out on their own who are doing this brand of soul-searching, and realizing that their goals — "leave my nine-to-five and do whatever I want" are no longer my own.)</p>
<p>Right now, I think the answer for all of this is something like the following: <strong>I want Buttondown to grow and fluorish specifically to validate and proselytize a slightly different model of software company: one that uses all the amazing things about software (negligible marginal costs! the infinite breadth of the internet!) to build high-value, sustainable tools for customers and doing so in a way that is fair and rewarding to employees.</strong> It seems almost trivializing to write that, but it's legitimately true. I think the world would be a better place if there were more smaller, older software companies focused on craftsmanship, and I think the best way to help catalyze that world is by building such a company.</p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 13
- 0
cache/2024/c2a852eced710f481135a1f61cb67a26/index.md View File

@@ -0,0 +1,13 @@
title: Why should a company? · Applied Cartography
url: https://jmduke.com/posts/microblog/why-should-a-company/
hash_url: c2a852eced710f481135a1f61cb67a26
archive_date: 2024-03-28
og_image: https://jmduke.com/img/share.png
description:
favicon: https://jmduke.com/favicon.ico
language: en_US

<p>For a long time, my goal with Buttondown was largely around failure avoidance: "I want to get my first paying customer so I know it's not a fake product"; "I want to hit a thousand dollars in revenue so I know it's not just friends humoring me"; "I want to make $10K/mo or else I won't be able to work on it full-time if I want to"; and so on. It's now at the point where those kinds of milestones don't really exist: it's profitable enough to support my salary and the salary of others, it grows every month.</p>
<p>As such, I've become more attuned to what my goal really <em>should be</em> with Buttondown. Most tech companies have a goal centered around some flavor of maximalism: disrupting an industry, revolutionizing a process, that kind of thing. Buttondown is not that kind of product; I am not that kind of person.</p>
<p>And yet I <em>do</em> want Buttondown to grow larger than it currently is: not by leaps and bounds, of course, but there are things that I think <em>make it a better product</em> that require further investment and I genuinely think there are a lot of people out there who would be happier using Buttondown than using their current tool. Both of those things imply a size of company that is <em>larger</em> than Buttondown's current size. (I'm also having a lot of conversations with folks considering striking out on their own who are doing this brand of soul-searching, and realizing that their goals — "leave my nine-to-five and do whatever I want" are no longer my own.)</p>
<p>Right now, I think the answer for all of this is something like the following: <strong>I want Buttondown to grow and fluorish specifically to validate and proselytize a slightly different model of software company: one that uses all the amazing things about software (negligible marginal costs! the infinite breadth of the internet!) to build high-value, sustainable tools for customers and doing so in a way that is fair and rewarding to employees.</strong> It seems almost trivializing to write that, but it's legitimately true. I think the world would be a better place if there were more smaller, older software companies focused on craftsmanship, and I think the best way to help catalyze that world is by building such a company.</p>

+ 10
- 0
cache/2024/index.html View File

@@ -76,6 +76,8 @@
<li><a href="/david/cache/2024/877ad04fd329c26c80113e15dec540df/" title="Accès à l’article dans le cache local : The Walk and Talk: Everything We Know">The Walk and Talk: Everything We Know</a> (<a href="https://craigmod.com/ridgeline/176/" title="Accès à l’article original distant : The Walk and Talk: Everything We Know">original</a>)</li>
<li><a href="/david/cache/2024/a11451e038f3aedba0ae55ff242e1ed5/" title="Accès à l’article dans le cache local : “I’m not a cynic, I’m disappointed”">“I’m not a cynic, I’m disappointed”</a> (<a href="https://www.baldurbjarnason.com/2024/the-software-crisis-easter-sale/" title="Accès à l’article original distant : “I’m not a cynic, I’m disappointed”">original</a>)</li>
<li><a href="/david/cache/2024/f68e9507784b5baf1584085908d60f58/" title="Accès à l’article dans le cache local : Hyperlink escape codes">Hyperlink escape codes</a> (<a href="https://notes.billmill.org/programming/bash/Hyperlink_escape_codes.html" title="Accès à l’article original distant : Hyperlink escape codes">original</a>)</li>
<li><a href="/david/cache/2024/4e116948ed4d26daa981a6c4ea9e4282/" title="Accès à l’article dans le cache local : Write CSS. Not too much. Mostly scoped.">Write CSS. Not too much. Mostly scoped.</a> (<a href="https://www.leereamsnyder.com/write-css-not-too-much-mostly-scoped" title="Accès à l’article original distant : Write CSS. Not too much. Mostly scoped.">original</a>)</li>
@@ -210,8 +212,12 @@
<li><a href="/david/cache/2024/4a9c4c407b34c40ec5b3783ac5f274a7/" title="Accès à l’article dans le cache local : Three requests for the Google Chrome team as they experiment with RSS">Three requests for the Google Chrome team as they experiment with RSS</a> (<a href="https://interconnected.org/home/2021/05/26/chrome_and_rss" title="Accès à l’article original distant : Three requests for the Google Chrome team as they experiment with RSS">original</a>)</li>
<li><a href="/david/cache/2024/2de5f6111316a8158795d70666b55a95/" title="Accès à l’article dans le cache local : Facebook snooped on users’ Snapchat traffic in secret project, documents reveal">Facebook snooped on users’ Snapchat traffic in secret project, documents reveal</a> (<a href="https://techcrunch.com/2024/03/26/facebook-secret-project-snooped-snapchat-user-traffic/" title="Accès à l’article original distant : Facebook snooped on users’ Snapchat traffic in secret project, documents reveal">original</a>)</li>
<li><a href="/david/cache/2024/1f4e359d100592aee6e46505a40b2a01/" title="Accès à l’article dans le cache local : Playing with Infinity in CSS">Playing with Infinity in CSS</a> (<a href="https://codersblock.com/blog/playing-with-infinity-in-css/" title="Accès à l’article original distant : Playing with Infinity in CSS">original</a>)</li>
<li><a href="/david/cache/2024/422adbcbb23d5d87eb9d9f07f21e6e52/" title="Accès à l’article dans le cache local : Growth is a mind cancer">Growth is a mind cancer</a> (<a href="https://manuelmoreale.com/growth-is-a-mind-cancer" title="Accès à l’article original distant : Growth is a mind cancer">original</a>)</li>
<li><a href="/david/cache/2024/529fce4c2b7c378f07aead94e62d3923/" title="Accès à l’article dans le cache local : Some little ways I’m using CSS :has() in the real world">Some little ways I’m using CSS :has() in the real world</a> (<a href="https://piccalil.li/blog/some-little-ways-im-using-css-has-in-the-real-world/" title="Accès à l’article original distant : Some little ways I’m using CSS :has() in the real world">original</a>)</li>
<li><a href="/david/cache/2024/89dbef9daef24f311b6401cef62f5855/" title="Accès à l’article dans le cache local : Des Oloés">Des Oloés</a> (<a href="https://tw5.immateriel.fr/wiki/immateriel/b/YXGEDFB" title="Accès à l’article original distant : Des Oloés">original</a>)</li>
@@ -230,6 +236,8 @@
<li><a href="/david/cache/2024/ddc3ee3603be2702cef745c180b87776/" title="Accès à l’article dans le cache local : TousAntiCovid : vu depuis Codeureuses en Liberté">TousAntiCovid : vu depuis Codeureuses en Liberté</a> (<a href="https://www.codeursenliberte.fr/blog/tous_anti_covid/" title="Accès à l’article original distant : TousAntiCovid : vu depuis Codeureuses en Liberté">original</a>)</li>
<li><a href="/david/cache/2024/821fa933883f080d23c0a6d9d0b3721a/" title="Accès à l’article dans le cache local : Make">Make</a> (<a href="https://www.arthurperret.fr/cours/make.html" title="Accès à l’article original distant : Make">original</a>)</li>
<li><a href="/david/cache/2024/85b765a918ef094a5a2dd13a1ff5dd7d/" title="Accès à l’article dans le cache local : RSS 2.0 Specification">RSS 2.0 Specification</a> (<a href="https://www.rssboard.org/rss-specification#extendingRss" title="Accès à l’article original distant : RSS 2.0 Specification">original</a>)</li>
<li><a href="/david/cache/2024/ad911ebf7ba5523ef0be1bdd599f7623/" title="Accès à l’article dans le cache local : JavaScript Bloat in 2024">JavaScript Bloat in 2024</a> (<a href="https://tonsky.me/blog/js-bloat/" title="Accès à l’article original distant : JavaScript Bloat in 2024">original</a>)</li>
@@ -298,6 +306,8 @@
<li><a href="/david/cache/2024/64ec164fc6c5c23ede3fba6006a47f36/" title="Accès à l’article dans le cache local : The quiet, pervasive devaluation of frontend">The quiet, pervasive devaluation of frontend</a> (<a href="https://joshcollinsworth.com/blog/devaluing-frontend" title="Accès à l’article original distant : The quiet, pervasive devaluation of frontend">original</a>)</li>
<li><a href="/david/cache/2024/c2a852eced710f481135a1f61cb67a26/" title="Accès à l’article dans le cache local : Why should a company? · Applied Cartography">Why should a company? · Applied Cartography</a> (<a href="https://jmduke.com/posts/microblog/why-should-a-company/" title="Accès à l’article original distant : Why should a company? · Applied Cartography">original</a>)</li>
<li><a href="/david/cache/2024/2cbc47f0ebded9d54fe6163fa4ea0667/" title="Accès à l’article dans le cache local : Where I’m at on the whole CSS-Tricks thing">Where I’m at on the whole CSS-Tricks thing</a> (<a href="https://chriscoyier.net/2024/02/28/where-im-at-on-the-whole-css-tricks-thing/" title="Accès à l’article original distant : Where I’m at on the whole CSS-Tricks thing">original</a>)</li>
<li><a href="/david/cache/2024/e8748af541273328d9aa9f1aeb1087b2/" title="Accès à l’article dans le cache local : Redeployment Part Three">Redeployment Part Three</a> (<a href="https://brr.fyi/posts/redeployment-part-three" title="Accès à l’article original distant : Redeployment Part Three">original</a>)</li>

Loading…
Cancel
Save