-
-
Notifications
You must be signed in to change notification settings - Fork 10k
/
Copy pathFormula-Cookbook.md
1278 lines (890 loc) · 67.6 KB
/
Formula-Cookbook.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Formula Cookbook
A *formula* is a package definition written in Ruby. It can be created with `brew create <URL>` where `<URL>` is a zip or tarball, installed with `brew install <formula>`, and debugged with `brew install --debug --verbose <formula>`. Formulae use the [Formula API](https://rubydoc.brew.sh/Formula) which provides various Homebrew-specific helpers.
* Table of Contents
{:toc}
## Homebrew terminology
| term | description | example |
| -------------------- | ------------------------------------------------------------------------- | ------- |
| **formula** | Homebrew package definition that builds from upstream sources | `/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb` |
| **cask** | Homebrew package definition that installs macOS native applications | `/usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask/Casks/b/bar.rb` |
| **prefix** | path in which Homebrew is installed | `/usr/local` |
| **keg** | installation destination directory of a given **formula** version | `/usr/local/Cellar/foo/0.1` |
| **rack** | directory containing one or more versioned **kegs** | `/usr/local/Cellar/foo` |
| **keg-only** | a **formula** is *keg-only* if it is not symlinked into Homebrew's prefix | the [`openjdk`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/o/openjdk.rb) formula |
| **opt prefix** | a symlink to the active version of a **keg** | `/usr/local/opt/foo` |
| **Cellar** | directory containing one or more named **racks** | `/usr/local/Cellar` |
| **Caskroom** | directory containing one or more named **casks** | `/usr/local/Caskroom` |
| **external command** | `brew` subcommand defined outside of the Homebrew/brew GitHub repository | [`brew alias`](https://github.com/Homebrew/homebrew-aliases) |
| **tap** | directory (and usually Git repository) of **formulae**, **casks** and/or **external commands** | `/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core` |
| **bottle** | pre-built **keg** poured into a **rack** of the **Cellar** instead of building from upstream sources | `qt--6.5.1.ventura.bottle.tar.gz` |
| **tab** | information about a **keg**, e.g. whether it was poured from a **bottle** or built from source | `/usr/local/Cellar/foo/0.1/INSTALL_RECEIPT.json` |
| **Brew Bundle** | an [extension of Homebrew](https://github.com/Homebrew/homebrew-bundle) to describe dependencies | `brew 'myservice', restart_service: true` |
| **Brew Services** | an [extension of Homebrew](https://github.com/Homebrew/homebrew-services) to manage services | `brew services start myservice` |
## An introduction
Homebrew uses Git for storing formulae and contributing to the project.
As of [Homebrew 4.0.0](https://brew.sh/2023/02/16/homebrew-4.0.0/), formulae are downloaded as JSON from <https://formulae.brew.sh> which is automatically regenerated by a scheduled [Homebrew/formulae.brew.sh](https://github.com/Homebrew/formulae.brew.sh) job from the `master` branch of the Homebrew/homebrew-core repository.
Homebrew installs formulae to the Cellar at `$(brew --cellar)` and then symlinks some of the installation into the prefix at `$(brew --prefix)` (e.g. `/opt/homebrew`) so that other programs can see what's going on. We suggest running `brew ls` on a few of the kegs in your Cellar to see how it is all arranged.
Packages are installed according to their formulae. Read over a simple one, e.g. `brew edit etl` (or [etl.rb](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/e/etl.rb)) or a more advanced one, e.g. `brew edit git` (or [git.rb](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/g/git.rb)).
## Basic instructions
Make sure you run `brew update` before you start. This ensures your Homebrew installation is a Git repository.
To create or edit formulae locally, you'll need to first [tap `homebrew/core`](https://docs.brew.sh/FAQ#can-i-edit-formulae-myself) if you haven't previously. This clones the Homebrew/homebrew-core Git repository to `$(brew --repository homebrew/core)`. As you're developing, you'll also need to set `HOMEBREW_NO_INSTALL_FROM_API=1` in your shell environment or before any `install`, `reinstall` or `upgrade` commands to force `brew` to use the local repository instead of the API.
Before submitting a new formula make sure your package:
* meets all our [Acceptable Formulae](Acceptable-Formulae.md) requirements
* isn't already in Homebrew (check `brew search <formula>`)
* isn't already waiting to be merged (check the [issue tracker](https://github.com/Homebrew/homebrew-core/pulls))
* is still supported by upstream (i.e. doesn't require extensive patching)
* has a stable, tagged version (i.e. isn't just a GitHub repository with no versions)
* passes all `brew audit --new --formula <formula>` tests
Before submitting a new formula make sure you read over our [contribution guidelines](https://github.com/Homebrew/brew/blob/HEAD/CONTRIBUTING.md#contributing-to-homebrew).
### Grab the URL
Run `brew create` with a URL to the source tarball:
```sh
brew create https://example.com/foo-0.1.tar.gz
```
This creates `$(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb` and opens it in your `EDITOR`.
Passing in `--ruby` or `--python` will populate various defaults commonly useful for projects written in those languages.
If `brew` said `Warning: Version cannot be determined from URL` when doing the `create` step, you’ll need to explicitly add the correct [`version`](https://rubydoc.brew.sh/Formula#version-class_method) to the formula and then save the formula.
Homebrew will try to guess the formula’s name from its URL. If it fails to do so you can override this with `brew create <URL> --set-name <name>`.
### Fill in the `homepage`
**We don’t accept formulae without a [`homepage`](https://rubydoc.brew.sh/Formula#homepage%3D-class_method)!**
An SSL/TLS (https) [`homepage`](https://rubydoc.brew.sh/Formula#homepage%3D-class_method) is preferred, if one is available.
Try to summarise from the [`homepage`](https://rubydoc.brew.sh/Formula#homepage%3D-class_method) what the formula does in the [`desc`](https://rubydoc.brew.sh/Formula#desc%3D-class_method)ription. Note that the [`desc`](https://rubydoc.brew.sh/Formula#desc%3D-class_method)ription is automatically prepended with the formula name when printed.
### Fill in the `license`
**We don’t accept new formulae into Homebrew/homebrew-core without a [`license`](https://rubydoc.brew.sh/Formula#license-class_method)!**
We only accept formulae that use a [Debian Free Software Guidelines license](https://wiki.debian.org/DFSGLicenses) or are released into the public domain following [DFSG Guidelines on Public Domain software](https://wiki.debian.org/DFSGLicenses#Public_Domain).
Use the license identifier from the [SPDX License List](https://spdx.org/licenses/) e.g. `license "BSD-2-Clause"`, or use `license :public_domain` for public domain software.
Use `:any_of`, `:all_of` or `:with` to describe complex license expressions. `:any_of` should be used when the user can choose which license to use. `:all_of` should be used when the user must use all licenses. `:with` should be used to specify a valid SPDX exception. Add `+` to an identifier to indicate that the formulae can be licensed under later versions of the same license.
Check out the [License Guidelines](License-Guidelines.md) for examples of complex license expressions in Homebrew formulae.
### Check the build system
```sh
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --interactive foo
```
You’re now at a new prompt with the tarball extracted to a temporary sandbox.
Check the package’s `README`. Does the package install with `./configure`, `cmake`, or something else? Delete the commented out `cmake` lines if the package uses `./configure`.
### Check for dependencies
The `README` probably tells you about dependencies and Homebrew or macOS probably already has them. You can check for Homebrew dependencies with `brew search`. Some common dependencies that macOS comes with:
* `libexpat`
* `libGL`
* `libiconv`
* `libpcap`
* `libxml2`
* `python`
* `ruby`
There are plenty of others; check `/usr/lib` for them.
We generally try not to duplicate system libraries and complicated tools in core Homebrew but we do duplicate some commonly used tools.
Special exceptions are OpenSSL and LibreSSL. Things that use either *should* be built using Homebrew’s shipped equivalent and our BrewTestBot's post-install `audit` will warn if it detects you haven't done this.
**Important:** `$(brew --prefix)/bin` is NOT in the `PATH` during formula installation. If you have dependencies at build time, you must specify them and `brew` will add them to the `PATH` or create a [`Requirement`](https://rubydoc.brew.sh/Requirement).
### Specifying other formulae as dependencies
```ruby
class Foo < Formula
# ...
depends_on "httpd" => [:build, :test]
depends_on xcode: ["9.3", :build]
depends_on arch: :x86_64
depends_on "jpeg"
depends_on macos: :high_sierra
depends_on "pkg-config"
depends_on "readline" => :recommended
depends_on "gtk+" => :optional
# ...
end
```
A `String` (e.g. `"jpeg"`) specifies a formula dependency.
A `Symbol` (e.g. `:xcode`) specifies a [`Requirement`](https://rubydoc.brew.sh/Requirement) to restrict installation to systems meeting certain criteria, which can be fulfilled by one or more formulae, casks or other system-wide installed software (e.g. Xcode). Some [`Requirement`](https://rubydoc.brew.sh/Requirement)s can also take a string or symbol specifying their minimum version that the formula depends on.
A `Hash` (e.g. `=>`) adds information to a dependency. Given a string or symbol, the value can be one or more of the following values:
* `:build` means this is a build-time only dependency so it can be skipped when installing from a bottle or when listing missing dependencies using `brew missing`.
* `:test` means this is only required when running `brew test`.
* `:optional` (not allowed in `Homebrew/homebrew-core`) generates an implicit `with-foo` option for the formula. This means that, given `depends_on "foo" => :optional`, the user must pass `--with-foo` to use the dependency.
* `:recommended` (not allowed in `Homebrew/homebrew-core`) generates an implicit `without-foo` option, meaning that the dependency is enabled by default and the user must pass `--without-foo` to disable this dependency. The default description can be overridden using the [`option`](https://rubydoc.brew.sh/Formula#option-class_method) syntax (in this case, the [`option` declaration](#adding-optional-steps) must precede the dependency):
```ruby
option "with-foo", "Compile with foo bindings" # This overrides the generated description if you want to
depends_on "foo" => :optional # Generated description would otherwise be "Build with foo support"
```
* `"<option-name>"` (not allowed in `Homebrew/homebrew-core`) requires a dependency to have been built with the specified option.
### Specifying conflicts with other formulae
Sometimes there’s a hard conflict between formulae that can’t be avoided or circumvented with [`keg_only`](https://rubydoc.brew.sh/Formula#keg_only-class_method).
A good example for minor conflict is the [`mbedtls`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/m/mbedtls.rb) formula, which ships and compiles a "Hello World" executable. This is obviously non-essential to `mbedtls`’s functionality, and as conflict with the popular GNU [`hello`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/h/hello.rb) formula would be overkill, we just [remove it](https://github.com/Homebrew/homebrew-core/blob/442f9cc511ce6dfe75b96b2c83749d90dde914d2/Formula/m/mbedtls.rb#L52-L53) during the installation process.
[`pdftohtml`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/p/pdftohtml.rb) provides an example of a serious conflict, where each listed formula ships an identically named binary that is essential to functionality, so a [`conflicts_with`](https://rubydoc.brew.sh/Formula#conflicts_with-class_method) is preferable.
As a general rule, [`conflicts_with`](https://rubydoc.brew.sh/Formula#conflicts_with-class_method) should be a last-resort option. It’s a fairly blunt instrument.
The syntax for a conflict that can’t be worked around is:
```ruby
conflicts_with "blueduck", because: "yellowduck also ships a duck binary"
```
### Formulae revisions
In Homebrew we sometimes accept formulae updates that don’t include a version bump. These include resource updates, new patches or fixing a security issue with a formula.
Occasionally, these updates require a forced-recompile of the formula itself or its dependents to either ensure formulae continue to function as expected or to close a security issue. This forced-recompile is known as a [`revision`](https://rubydoc.brew.sh/Formula#revision%3D-class_method) and is inserted underneath the [`homepage`](https://rubydoc.brew.sh/Formula#homepage%3D-class_method)/[`url`](https://rubydoc.brew.sh/Formula#url-class_method)/[`sha256`](https://rubydoc.brew.sh/Formula#sha256%3D-class_method)/[`license`](https://rubydoc.brew.sh/Formula#license-class_method) block.
When a dependent of a formula fails to build against a new version of that dependency it must receive a [`revision`](https://rubydoc.brew.sh/Formula#revision%3D-class_method). An example of such failure is in [this issue report](https://github.com/Homebrew/legacy-homebrew/issues/31195) and [its fix](https://github.com/Homebrew/legacy-homebrew/pull/31207).
[`revision`](https://rubydoc.brew.sh/Formula#revision%3D-class_method)s are also used for formulae that move from the system OpenSSL to the Homebrew-shipped OpenSSL without any other changes to that formula. This ensures users aren’t left exposed to the potential security issues of the outdated OpenSSL. An example of this can be seen in [this commit](https://github.com/Homebrew/homebrew-core/commit/0d4453a91923e6118983961e18d0609e9828a1a4).
### Version scheme changes
Sometimes formulae have version schemes that change such that a direct comparison between two versions no longer produces the correct result. For example, a project might be version `13` and then decide to become `1.0.0`. As `13` is translated to `13.0.0` by our versioning system by default this requires intervention.
When a version scheme of a formula fails to recognise a new version as newer it must receive a [`version_scheme`](https://rubydoc.brew.sh/Formula#version_scheme%3D-class_method). An example of this can be seen in [this pull request](https://github.com/Homebrew/homebrew-core/pull/4006).
### Double-check for dependencies
When you already have a lot of formulae installed, it's easy to miss a common dependency. You can double-check which libraries a binary links to with the `otool` command (perhaps you need to use `xcrun otool`):
```console
$ otool -L /usr/local/bin/ldapvi
/usr/local/bin/ldapvi:
/usr/local/opt/openssl/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/local/lib/libglib-2.0.0.dylib (compatibility version 4201.0.0, current version 4201.0.0)
/usr/local/opt/gettext/lib/libintl.8.dylib (compatibility version 10.0.0, current version 10.2.0)
/usr/local/opt/readline/lib/libreadline.6.dylib (compatibility version 6.0.0, current version 6.3.0)
/usr/local/lib/libpopt.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/System/Library/Frameworks/LDAP.framework/Versions/A/LDAP (compatibility version 1.0.0, current version 2.4.0)
/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
```
### Specifying macOS components as dependencies
If a formula dependency is required on all platforms but can be handled by a component that ships with macOS, specify it with [`uses_from_macos`](https://rubydoc.brew.sh/Formula#uses_from_macos-class_method). On Linux it acts like [`depends_on`](https://rubydoc.brew.sh/Formula#depends_on-class_method), while on macOS it's ignored unless the host system is older than the optional `since:` parameter.
For example, to require the `bzip2` formula on Linux while relying on built-in `bzip2` on macOS:
```ruby
uses_from_macos "bzip2"
```
To require the `perl` formula only when building or testing on Linux:
```ruby
uses_from_macos "perl" => [:build, :test]
```
To require the `curl` formula on Linux and pre-macOS 12:
```ruby
uses_from_macos "curl", since: :monterey
```
### Specifying gems, Python modules, Go projects, etc. as dependencies
Homebrew doesn’t package already-packaged language-specific libraries. These should be installed directly from `gem`/`cpan`/`pip` etc.
### Ruby Gem Dependencies
The preferred mechanism for installing gem dependencies is to use `bundler` with the upstream's `Gemfile.lock`. This requires the upstream checks in their `Gemfile.lock`, so if they don't, it's a good idea to file an issue and ask them to do so. Assuming they have one, this is as simple as:
```ruby
ENV["GEM_HOME"] = libexec
system "bundle", "install", "--without", "development"
```
From there, you can build and install the project itself:
```ruby
system "gem", "build", "<project>.gemspec"
system "gem", "install", "--ignore-dependencies", "<project>-#{version}.gem"
```
And install any bins, and munge their shebang lines, with:
```ruby
bin.install libexec/"bin/<bin>"
bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV.fetch("GEM_HOME", nil))
```
### Python dependencies
For python we use [`resource`](https://rubydoc.brew.sh/Formula#resource-class_method)s for dependencies and there's automation to generate these for you. Running `brew update-python-resources <formula>` will automatically add the necessary [`resource`](https://rubydoc.brew.sh/Formula#resource-class_method) stanzas for the dependencies of your Python application to the formula. Note that `brew update-python-resources` is run automatically by `brew create` if you pass the `--python` switch. If `brew update-python-resources` is unable to determine the correct `resource` stanzas, [homebrew-pypi-poet](https://github.com/tdsmith/homebrew-pypi-poet) is a good third-party alternative that may help.
### All other cases
If all else fails, you'll want to use [`resource`](https://rubydoc.brew.sh/Formula#resource-class_method)s for all other language-specific dependencies. This requires you to specify both a specific URL for a version and the sha256 checksum for security. Here's an example:
```ruby
class Foo < Formula
# ...
url "https://example.com/foo-1.0.tar.gz"
resource "pycrypto" do
url "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz"
sha256 "f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"
end
def install
resource("pycrypto").stage { system "python", *Language::Python.setup_install_args(libexec/"vendor") }
end
end
```
[`jrnl`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/j/jrnl.rb) is an example of a formula that does this well. The end result means the user doesn't have to use `pip` or Python and can just run `jrnl`.
### Install the formula
```sh
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --verbose --debug foo
```
`--debug` will ask you to open an interactive shell if the build fails so you can try to figure out what went wrong.
Check the top of the e.g. `./configure` output. Some configure scripts do not recognise e.g. `--disable-debug`. If you see a warning about it, remove the option from the formula.
### Add a test to the formula
Add a valid test to the [`test do`](https://rubydoc.brew.sh/Formula#test-class_method) block of the formula. This will be run by `brew test foo` and [BrewTestBot](BrewTestBot.md).
The [`test do`](https://rubydoc.brew.sh/Formula#test-class_method) block automatically creates and changes to a temporary directory which is deleted after run. You can access this [`Pathname`](https://rubydoc.brew.sh/Pathname) with the [`testpath`](https://rubydoc.brew.sh/Formula#testpath-instance_method) function. The environment variable `HOME` is set to [`testpath`](https://rubydoc.brew.sh/Formula#testpath-instance_method) within the [`test do`](https://rubydoc.brew.sh/Formula#test-class_method) block.
We want tests that don't require any user input and test the basic functionality of the application. For example `foo build-foo input.foo` is a good test and (despite their widespread use) `foo --version` and `foo --help` are bad tests. However, a bad test is better than no test at all.
See the [`cmake`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/c/cmake.rb) formula for an example of a good test. It writes a basic `CMakeLists.txt` file into the test directory then calls CMake to generate Makefiles. This test checks that CMake doesn't e.g. segfault during basic operation.
You can check that the output is as expected with `assert_equal` or `assert_match` on the output of the [Formula assertions](https://rubydoc.brew.sh/Homebrew/Assertions) such as in this example from the [`envv`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/e/envv.rb) formula:
```ruby
assert_equal "mylist=A:C; export mylist", shell_output("#{bin}/envv del mylist B").strip
```
You can also check that an output file was created:
```ruby
assert_predicate testpath/"output.txt", :exist?
```
Some advice for specific cases:
* If the formula is a library, compile and run some simple code that links against it. It could be taken from upstream's documentation / source examples. A good example is [`tinyxml2`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/t/tinyxml2.rb)'s test, which writes a small C++ source file into the test directory, compiles and links it against the tinyxml2 library and finally checks that the resulting program runs successfully.
* If the formula is for a GUI program, try to find some function that runs as command-line only, like a format conversion, reading or displaying a config file, etc.
* If the software cannot function without credentials or requires a virtual machine, docker instance, etc. to run, a test could be to try to connect with invalid credentials (or without credentials) and confirm that it fails as expected. This is preferred over mocking a dependency.
* Homebrew comes with a number of [standard test fixtures](https://github.com/Homebrew/brew/tree/master/Library/Homebrew/test/support/fixtures), including numerous sample images, sounds, and documents in various formats. You can get the file path to a test fixture with e.g. `test_fixtures("test.svg")`.
* If your test requires a test file that isn't a standard test fixture, you can install it from a source repository during the `test` phase with a [`resource`](https://rubydoc.brew.sh/Formula#resource-class_method) block, like this:
```ruby
test do
resource "testdata" do
url "https://example.com/input.foo"
sha256 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
end
resource("testdata").stage do
assert_match "OK", shell_output("#{bin}/foo build-foo input.foo")
end
end
```
### Manuals
Homebrew expects to find manual pages in `#{prefix}/share/man/...`, and not in `#{prefix}/man/...`.
Some software installs to `man` instead of `share/man`, so check the output and add a `"--mandir=#{man}"` to the `./configure` line if needed.
### Caveats
In case there are specific issues with the Homebrew packaging (compared to how the software is installed from other sources) a `caveats` block can be added to the formula to warn users. This can indicate non-standard install paths, like this example from the [`ruby`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/r/ruby.rb) formula:
==> Caveats
By default, binaries installed by gem will be placed into:
/usr/local/lib/ruby/gems/bin
You may want to add this to your PATH.
### A quick word on naming
Name the formula like the project markets the product. So it’s `pkg-config`, not `pkgconfig`; `sdl_mixer`, not `sdl-mixer` or `sdlmixer`.
The only exception is stuff like “Apache Ant”. Apache sticks “Apache” in front of everything, but we use the formula name `ant`. We only include the prefix in cases like `gnuplot` (because it’s part of the name) and `gnu-go` (because everyone calls it “GNU Go”—nobody just calls it “Go”). The word “Go” is too common and there are too many implementations of it.
If you’re not sure about the name, check its homepage, Wikipedia page and [what Debian calls it](https://www.debian.org/distrib/packages).
When Homebrew already has a formula called `foo` we typically do not accept requests to replace that formula with something else also named `foo`. This is to avoid both confusing and surprising users’ expectations.
When two formulae share an upstream name, e.g. [AESCrypt](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/a/aescrypt.rb) and [AES Crypt](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/a/aescrypt-packetizer.rb) the newer formula must typically adapt its name to avoid conflict with the current formula.
If you’re *still* not sure, just commit. We’ll apply some arbitrary rule and make a decision 😉.
When importing classes, Homebrew will require the formula and then create an instance of the class. It does this by assuming the formula name can be directly converted to the class name using a `regexp`. The rules are simple:
* `foo-bar.rb` => `FooBar`
* `foobar.rb` => `Foobar`
Thus, if you change the name of the class, you must also rename the file. Filenames should be all lowercase, and class names should be the strict CamelCase equivalent, e.g. formulae `gnu-go` and `sdl_mixer` become classes `GnuGo` and `SdlMixer`, even if part of their name is an acronym.
Add aliases by creating symlinks in an `Aliases` directory in the tap root.
### Audit the formula
You can run `brew audit --strict --online` to test formulae for adherence to Homebrew house style, which is loosely based on the [Ruby Style Guide](https://github.com/rubocop-hq/ruby-style-guide#the-ruby-style-guide). The `audit` command includes warnings for trailing whitespace, preferred URLs for certain source hosts, and many other style issues. Fixing these warnings before committing will make the process a lot quicker for everyone.
New formulae being submitted to Homebrew should run `brew audit --new --formula foo`. This command is performed by BrewTestBot on new submissions as part of the automated build and test process, and highlights more potential issues than the standard audit.
Use `brew info` and check if the version guessed by Homebrew from the URL is correct. Add an explicit [`version`](https://rubydoc.brew.sh/Formula#version-class_method) if not.
### Commit
Everything is built on Git, so contribution is easy:
```sh
brew update # required in more ways than you think (initialises the Homebrew/brew Git repository if you don't already have it)
cd "$(brew --repository homebrew/core)"
# Create a new git branch for your formula so your pull request is easy to
# modify if any changes come up during review.
git checkout -b <some-descriptive-name> origin/master
git add Formula/f/foo.rb
git commit
```
The established standard for Git commit messages is:
* the first line is a commit summary of *50 characters or less*
* two (2) newlines, then
* explain the commit thoroughly.
At Homebrew, we require the name of the formula up front like so: `foobar 7.3 (new formula)`.
This may seem crazy short, but you’ll find that forcing yourself to summarise the commit encourages you to be atomic and concise. If you can’t summarise it in 50 to 80 characters, you’re probably trying to commit two commits as one. For a more thorough explanation, please read Tim Pope’s excellent blog post, [A Note About Git Commit Messages](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
The required commit message format for simple version updates is `foobar 7.3` and for fixes is `foobar: fix flibble matrix.`. Please squash your commits into one with this message format, otherwise your PR will be replaced by our autosquash workflow.
Ensure you reference any relevant GitHub issue, e.g. `Closes #12345` in the commit message. Homebrew’s history is the first thing future contributors will look to when trying to understand the current state of formulae they’re interested in.
### Push
Now you just need to push your commit to GitHub.
If you haven’t forked Homebrew yet, [go to the Homebrew/homebrew-core repository and hit the Fork button](https://github.com/Homebrew/homebrew-core).
If you have already forked Homebrew on GitHub, then you can manually push (just make sure you have been pulling from the `Homebrew/homebrew-core` master):
```sh
git push https://github.com/myname/homebrew-core/ <what-you-named-your-branch>
```
Now, [open a pull request](https://docs.brew.sh/How-To-Open-a-Homebrew-Pull-Request) for your changes.
* One formula per commit; one commit per formula.
* Keep merge commits out of the pull request.
## Convenience tools
### Messaging
Three commands are provided for displaying informational messages to the user:
* `ohai` for general info
* `opoo` for warning messages
* `odie` for error messages and immediately exiting
Use `odie` when you need to exit a formula gracefully for any reason. For example:
```ruby
if build.head?
lib_jar = Dir["cfr-*-SNAPSHOT.jar"]
doc_jar = Dir["cfr-*-SNAPSHOT-javadoc.jar"]
odie "Unexpected number of artifacts!" if (lib_jar.length != 1) || (doc_jar.length != 1)
end
```
### Standard arguments
For any formula using certain well-known build systems, there will be arguments that should be passed during compilation so that the build conforms to Homebrew standards. These have been collected into a set of `std_*_args` methods.
Most of these methods accept parameters to customize their output. For example, to set the install prefix to [**`libexec`**](#variables-for-directory-locations) for `configure` or `cmake`:
```ruby
system "./configure", *std_configure_args(prefix: libexec)
system "cmake", "-S", ".", "-B", "build", *std_cmake_args(install_prefix: libexec)
```
The `std_*_args` methods, as well as the arguments they pass, are:
#### `std_cabal_v2_args`
```ruby
"--jobs=#{ENV.make_jobs}"
"--max-backjumps=100000"
"--install-method=copy"
"--installdir=#{bin}"
```
#### `std_cargo_args`
```ruby
"--locked"
"--root=#{root}"
"--path=#{path}"
```
#### `std_cmake_args`
```ruby
"-DCMAKE_INSTALL_PREFIX=#{install_prefix}"
"-DCMAKE_INSTALL_LIBDIR=#{install_libdir}"
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_FIND_FRAMEWORK=#{find_framework}"
"-DCMAKE_VERBOSE_MAKEFILE=ON"
"-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=#{HOMEBREW_LIBRARY_PATH}/cmake/trap_fetchcontent_provider.cmake"
"-Wno-dev"
"-DBUILD_TESTING=OFF"
```
#### `std_configure_args`
```ruby
"--disable-debug"
"--disable-dependency-tracking"
"--prefix=#{prefix}"
"--libdir=#{libdir}"
```
#### `std_go_args`
```ruby
"-trimpath"
"-o=#{output}"
```
#### `std_meson_args`
```ruby
"--prefix=#{prefix}"
"--libdir=#{lib}"
"--buildtype=release"
"--wrap-mode=nofallback"
```
#### `std_npm_args`
```ruby
"-ddd"
"--global"
"--build-from-source"
"--cache=$(brew --cache)/npm_cache"
"--prefix=#{libexec}"
```
#### `std_pip_args`
```ruby
"--verbose"
"--no-deps"
"--no-binary=:all:"
"--ignore-installed"
"--no-compile"
```
### `bin.install "foo"`
You’ll see stuff like this in some formulae. This moves the file `foo` into the formula’s `bin` directory (`/usr/local/Cellar/pkg/0.1/bin`) and makes it executable (`chmod 0555 foo`).
You can also rename the file during the installation process. This can be useful for adding a prefix to binaries that would otherwise cause conflicts with another formula, or for removing a file extension. For example, to install `foo.py` into the formula's `bin` directory (`/usr/local/Cellar/pkg/0.1/bin`) as just `foo` instead of `foo.py`:
```ruby
bin.install "foo.py" => "foo"
```
### `inreplace`
[`inreplace`](https://rubydoc.brew.sh/Utils/Inreplace) is a convenience function that can edit files in-place. For example:
```ruby
inreplace "path", before, after
```
`before` and `after` can be strings or regular expressions. You should use the block form if you need to make multiple replacements in a file:
```ruby
inreplace "path" do |s|
s.gsub!(/foo/, "bar")
s.gsub! "123", "456"
end
```
Make sure you modify `s`! This block ignores the returned value.
[`inreplace`](https://rubydoc.brew.sh/Utils/Inreplace) should be used instead of patches when patching something that will never be accepted upstream, e.g. making the software’s build system respect Homebrew’s installation hierarchy. If it's something that affects both Homebrew and MacPorts (i.e. macOS specific) it should be turned into an upstream submitted patch instead.
If you need to modify variables in a `Makefile`, rather than using [`change_make_var!`](https://rubydoc.brew.sh/StringInreplaceExtension.html#change_make_var!-instance_method) within an [`inreplace`](https://rubydoc.brew.sh/Utils/Inreplace), try passing them as arguments to `make`:
```ruby
system "make", "target", "VAR2=value1", "VAR2=value2", "VAR3=values can have spaces"
```
```ruby
system "make", "CC=#{ENV.cc}", "PREFIX=#{prefix}"
```
Note that values *can* contain unescaped spaces if you use the multiple-argument form of `system`.
## Patches
While [`patch`](https://rubydoc.brew.sh/Formula#patch-class_method)es should generally be avoided, sometimes they are temporarily necessary.
When [`patch`](https://rubydoc.brew.sh/Formula#patch-class_method)ing (i.e. fixing header file inclusion, fixing compiler warnings, etc.) the first thing to do is check whether the upstream project is aware of the issue. If not, file a bug report and/or submit your patch for inclusion. We may sometimes still accept your patch before it was submitted upstream but by getting the ball rolling on fixing the upstream issue you reduce the length of time we have to carry the patch around.
*Always justify a [`patch`](https://rubydoc.brew.sh/Formula#patch-class_method) with a code comment!* Otherwise, nobody will know when it is safe to remove the patch, or safe to leave it in when updating the formula. The comment should include a link to the relevant upstream issue(s).
External [`patch`](https://rubydoc.brew.sh/Formula#patch-class_method)es can be declared using resource-style blocks:
```ruby
patch do
url "https://example.com/example_patch.diff"
sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end
```
A strip level of `-p1` is assumed. It can be overridden using a symbol argument:
```ruby
patch :p0 do
url "https://example.com/example_patch.diff"
sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end
```
[`patch`](https://rubydoc.brew.sh/Formula#patch-class_method)es can be declared in [`stable`](https://rubydoc.brew.sh/Formula#stable-class_method) and [`head`](https://rubydoc.brew.sh/Formula#head-class_method) blocks. Always use a block instead of a conditional, i.e. `stable do ... end` instead of `if build.stable? then ... end`.
```ruby
stable do
# ...
patch do
url "https://example.com/example_patch.diff"
sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
end
end
```
Embedded (__END__) patches can be declared like so:
```ruby
patch :DATA
patch :p0, :DATA
```
with the patch data included at the end of the file:
__END__
diff --git a/foo/showfigfonts b/foo/showfigfonts
index 643c60b..543379c 100644
--- a/foo/showfigfonts
+++ b/foo/showfigfonts
@@ -14,6 +14,7 @@
…
Patches can also be embedded by passing a string. This makes it possible to provide multiple embedded patches while making only some of them conditional.
```ruby
patch :p0, "..."
```
In embedded patches, the string "HOMEBREW\_PREFIX" is replaced with the value of the constant `HOMEBREW_PREFIX` before the patch is applied.
In external patches, the string "@@HOMEBREW\_PREFIX@@" is replaced with the value of the constant `HOMEBREW_PREFIX` before the patch is applied.
### Creating the diff
```sh
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --interactive --git foo
# (make some edits)
git diff | pbcopy
brew edit foo
```
Now just paste into the formula after `__END__`.
Instead of `git diff | pbcopy`, for some editors `git diff >> path/to/your/formula/foo.rb` might help you ensure that the patch is not altered, e.g. whitespace removal, indentation changes, etc.
## Advanced formula tricks
See the [Formula API](https://rubydoc.brew.sh/Formula) for the full list of methods available within a formula. If anything isn’t clear, you can usually figure it out by `grep`ping the `$(brew --repository homebrew/core)` directory for examples. Please submit a pull request to amend this document if you think it will help!
### Handling different system configurations
Often, formulae need different dependencies, resources, patches, conflicts, deprecations or `keg_only` statuses on different OSes and architectures. In these cases, the components can be nested inside `on_macos`, `on_linux`, `on_arm` or `on_intel` blocks. For example, here's how to add `gcc` as a Linux-only dependency:
```ruby
on_linux do
depends_on "gcc"
end
```
Components can also be declared for specific macOS versions or version ranges. For example, to declare a dependency only on High Sierra, nest the `depends_on` call inside an `on_high_sierra` block. Add an `:or_older` or `:or_newer` parameter to the `on_high_sierra` method to add the dependency to all macOS versions that meet the condition. For example, to add `gettext` as a build dependency on Mojave and all later macOS versions, use:
```ruby
on_mojave :or_newer do
depends_on "gettext" => :build
end
```
Sometimes, a dependency is needed on certain macOS versions *and* on Linux. In these cases, a special `on_system` method can be used:
```ruby
on_system :linux, macos: :sierra_or_older do
depends_on "gettext" => :build
end
```
To check multiple conditions, nest the corresponding blocks. For example, the following code adds a `gettext` build dependency when on ARM *and* macOS:
```ruby
on_macos do
on_arm do
depends_on "gettext" => :build
end
end
```
#### Inside `def install` and `test do`
Inside `def install` and `test do`, don't use these `on_*` methods. Instead, use `if` statements and the following conditionals:
* `OS.mac?` and `OS.linux?` return `true` or `false` based on the OS
* `Hardware::CPU.intel?` and `Hardware::CPU.arm?` return `true` or `false` based on the arch
* `MacOS.version` returns the current macOS version. Use `==`, `<=` or `>=` to compare to symbols corresponding to macOS versions (e.g. `if MacOS.version >= :mojave`)
See the [`icoutils`](https://github.com/Homebrew/homebrew-core/blob/442f9cc511ce6dfe75b96b2c83749d90dde914d2/Formula/i/icoutils.rb#L36) formula for an example.
### `livecheck` blocks
When `brew livecheck` is unable to identify versions for a formula, we can control its behavior using a `livecheck` block. Here is a simple example to check a page for links containing a filename like `example-1.2.tar.gz`:
```ruby
livecheck do
url "https://www.example.com/downloads/"
regex(/href=.*?example[._-]v?(\d+(?:\.\d+)+)\.t/i)
end
```
For `url`/`regex` guidelines and additional `livecheck` block examples, refer to the [`brew livecheck` documentation](Brew-Livecheck.md). For more technical information on the methods used in a `livecheck` block, please refer to the [`Livecheck` class documentation](https://rubydoc.brew.sh/Livecheck).
### Unstable versions (`head`)
Formulae can specify an alternate download for the upstream project’s development cutting-edge source (e.g. `master`/`main`/`trunk`) using [`head`](https://rubydoc.brew.sh/Formula#head-class_method), which can be activated by passing `--HEAD` when installing. Specifying it is done in the same manner as [`url`](https://rubydoc.brew.sh/Formula#url-class_method):
```ruby
class Foo < Formula
# ...
head "https://github.com/some/package.git", branch: "main" # the default is "master"
end
```
You can also bundle the URL and any `head`-specific dependencies and resources in a `head do` block.
```ruby
class Foo < Formula
# ...
head do
url "https://svn.code.sf.net/p/project/code/trunk"
depends_on "pkg-config" => :build
end
end
```
You can test whether the [`head`](https://rubydoc.brew.sh/Formula#head-class_method) is being built with `build.head?` in the `install` method.
### URL download strategies
When parsing a download URL, Homebrew auto-detects the resource type it points to, whether archive (e.g. tarball, zip) or version control repository (e.g. Git, SVN, Mercurial) and chooses an appropriate download strategy. Some strategies can be passed additional options to alter what's downloaded. For example, to use a specific commit, tag, or branch from a repository, specify [`url`](https://rubydoc.brew.sh/Formula#url-class_method) or [`head`](https://rubydoc.brew.sh/Formula#head-class_method) with the `:tag` and `:revision`, `:revision`, or `:branch` options, like so:
```ruby
class Foo < Formula
# ...
url "https://github.com/some/package.git",
tag: "v1.6.2",
revision: "344cd2ee3463abab4c16ac0f9529a846314932a2"
end
```
If not inferable, specify which of Homebrew’s built-in download strategies to use with the `using:` option. For example:
```ruby
class Nginx < Formula
desc "HTTP(S) server and reverse proxy, and IMAP/POP3 proxy server"
homepage "https://nginx.org/"
url "https://nginx.org/download/nginx-1.23.2.tar.gz", using: :homebrew_curl
sha256 "a80cc272d3d72aaee70aa8b517b4862a635c0256790434dbfc4d618a999b0b46"
head "https://hg.nginx.org/nginx/", using: :hg
end
```
Homebrew offers these anonymous download strategies.
| `:using` value | download strategy |
| ---------------- | ----------------------------- |
| `:bzr` | `BazaarDownloadStrategy` |
| `:curl` | `CurlDownloadStrategy` |
| `:cvs` | `CVSDownloadStrategy` |
| `:fossil` | `FossilDownloadStrategy` |
| `:git` | `GitDownloadStrategy` |
| `:hg` | `MercurialDownloadStrategy` |
| `:homebrew_curl` | `HomebrewCurlDownloadStrategy` |
| `:nounzip` | `NoUnzipCurlDownloadStrategy` |
| `:post` | `CurlPostDownloadStrategy` |
| `:svn` | `SubversionDownloadStrategy` |
If you need more control over the way files are downloaded and staged, you can create a custom download strategy and specify it with the `:using` option:
```ruby
class MyDownloadStrategy < SomeHomebrewDownloadStrategy
def fetch(timeout: nil, **options)
opoo "Unhandled options in #{self.class}#fetch: #{options.keys.join(", ")}" unless options.empty?
# downloads output to `temporary_path`
end
end
class Foo < Formula
url "something", using: MyDownloadStrategy
end
```
### Compiler selection
Sometimes a package fails to build when using a certain compiler. Since recent [Xcode versions](Xcode.md) no longer include a GCC compiler we cannot simply force the use of GCC. Instead, the correct way to declare this is with the [`fails_with`](https://rubydoc.brew.sh/Formula#fails_with-class_method) DSL method. A properly constructed [`fails_with`](https://rubydoc.brew.sh/Formula#fails_with-class_method) block documents the latest compiler build version known to cause compilation to fail, and the cause of the failure. For example:
```ruby
fails_with :clang do
build 211
cause "Miscompilation resulting in segfault on queries"
end
fails_with :gcc do
version "5" # fails with GCC 5.x and earlier
cause "Requires C++17 support"
end
fails_with gcc: "7" do
version "7.1" # fails with GCC 7.0 and 7.1 but not 7.2, or any other major GCC version
cause <<-EOS
warning: dereferencing type-punned pointer will break strict-aliasing rules
Fixed in GCC 7.2, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42136
EOS
end
```
For `:clang`, `build` takes an integer (you can find this number in your `brew --config` output), while `:gcc` uses either just `version` which takes a string to indicate the last problematic GCC version, or a major version argument combined with `version` to single out a range of specific GCC releases. `cause` takes a string, and the use of heredocs is encouraged to improve readability and allow for more comprehensive documentation.
[`fails_with`](https://rubydoc.brew.sh/Formula#fails_with-class_method) declarations can be used with any of `:gcc`, `:llvm`, and `:clang`. Homebrew will use this information to select a working compiler (if one is available).
### Just moving some files
When your code in the install function is run, the current working directory is set to the extracted tarball. This makes it easy to just move some files:
```ruby
prefix.install "file1", "file2"
```
Or everything:
```ruby
prefix.install Dir["output/*"]
```
Or just the tarball's top-level files like README, LICENSE etc.:
```ruby
prefix.install_metafiles
```
Generally we'd rather you were specific about which files or directories need to be installed rather than installing everything.
#### Variables for directory locations
| name | default path | example |
| --------------------- | ---------------------------------------------- | ------- |
| **`HOMEBREW_PREFIX`** | output of `$(brew --prefix)` | `/usr/local` |
| **`prefix`** | `#{HOMEBREW_PREFIX}/Cellar/#{name}/#{version}` | `/usr/local/Cellar/foo/0.1` |
| **`opt_prefix`** | `#{HOMEBREW_PREFIX}/opt/#{name}` | `/usr/local/opt/foo` |
| **`bin`** | `#{prefix}/bin` | `/usr/local/Cellar/foo/0.1/bin` |
| **`doc`** | `#{prefix}/share/doc/#{name}` | `/usr/local/Cellar/foo/0.1/share/doc/foo` |
| **`include`** | `#{prefix}/include` | `/usr/local/Cellar/foo/0.1/include` |
| **`info`** | `#{prefix}/share/info` | `/usr/local/Cellar/foo/0.1/share/info` |
| **`lib`** | `#{prefix}/lib` | `/usr/local/Cellar/foo/0.1/lib` |
| **`libexec`** | `#{prefix}/libexec` | `/usr/local/Cellar/foo/0.1/libexec` |
| **`man`** | `#{prefix}/share/man` | `/usr/local/Cellar/foo/0.1/share/man` |
| **`man[1-8]`** | `#{prefix}/share/man/man[1-8]` | `/usr/local/Cellar/foo/0.1/share/man/man[1-8]` |
| **`sbin`** | `#{prefix}/sbin` | `/usr/local/Cellar/foo/0.1/sbin` |
| **`share`** | `#{prefix}/share` | `/usr/local/Cellar/foo/0.1/share` |
| **`pkgshare`** | `#{prefix}/share/#{name}` | `/usr/local/Cellar/foo/0.1/share/foo` |
| **`elisp`** | `#{prefix}/share/emacs/site-lisp/#{name}` | `/usr/local/Cellar/foo/0.1/share/emacs/site-lisp/foo` |
| **`frameworks`** | `#{prefix}/Frameworks` | `/usr/local/Cellar/foo/0.1/Frameworks` |
| **`kext_prefix`** | `#{prefix}/Library/Extensions` | `/usr/local/Cellar/foo/0.1/Library/Extensions` |
| **`zsh_function`** | `#{prefix}/share/zsh/site-functions` | `/usr/local/Cellar/foo/0.1/share/zsh/site-functions` |
| **`fish_function`** | `#{prefix}/share/fish/vendor_functions` | `/usr/local/Cellar/foo/0.1/share/fish/vendor_functions` |
| **`bash_completion`** | `#{prefix}/etc/bash_completion.d` | `/usr/local/Cellar/foo/0.1/etc/bash_completion.d` |
| **`zsh_completion`** | `#{prefix}/share/zsh/site-functions` | `/usr/local/Cellar/foo/0.1/share/zsh/site-functions` |
| **`fish_completion`** | `#{prefix}/share/fish/vendor_completions.d` | `/usr/local/Cellar/foo/0.1/share/fish/vendor_completions.d` |
| **`etc`** | `#{HOMEBREW_PREFIX}/etc` | `/usr/local/etc` |
| **`pkgetc`** | `#{HOMEBREW_PREFIX}/etc/#{name}` | `/usr/local/etc/foo` |
| **`var`** | `#{HOMEBREW_PREFIX}/var` | `/usr/local/var` |
| **`buildpath`** | temporary directory somewhere on your system | `/private/tmp/[formula-name]-0q2b/[formula-name]` |
These can be used, for instance, in code such as:
```ruby
bin.install Dir["output/*"]
```
to move binaries into their correct location within the Cellar, and:
```ruby
man.mkpath
```
to create the directory structure for the manual page location.
To install man pages into specific locations, use `man1.install "foo.1", "bar.1"`, `man2.install "foo.2"`, etc.
Note that in the context of Homebrew, [`libexec`](https://rubydoc.brew.sh/Formula#libexec-instance_method) is reserved for private use by the formula and therefore is not symlinked into `HOMEBREW_PREFIX`.
### File-level operations
You can use the file utilities provided by Ruby's [`FileUtils`](https://ruby-doc.org/stdlib-2.7.0/libdoc/fileutils/rdoc/FileUtils.html). These are included in the [`Formula`](https://rubydoc.brew.sh/Formula) class, so you do not need the `FileUtils.` prefix to use them.
When creating symlinks, take special care to ensure they are *relative* symlinks. This makes it easier to create a relocatable bottle. For example, to create a symlink in `bin` to an executable in `libexec`, use:
```ruby
bin.install_symlink libexec/"name"
```
instead of:
```ruby
ln_s libexec/"name", bin
```
The symlinks created by [`install_symlink`](https://rubydoc.brew.sh/Pathname#install_symlink-instance_method) are guaranteed to be relative. `ln_s` will only produce a relative symlink when given a relative path.
Several other utilities for Ruby's [`Pathname`](https://rubydoc.brew.sh/Pathname) can simplify some common operations.
* To perform several operations within a directory, enclose them within a [`cd <path> do`](https://rubydoc.brew.sh/Pathname#cd-instance_method) block:
```ruby
cd "src" do
system "./configure", "--disable-debug", "--prefix=#{prefix}"
system "make", "install"
end
```
* To surface one or more binaries buried in `libexec` or a macOS `.app` package, use [`write_exec_script`](https://rubydoc.brew.sh/Pathname#write_exec_script-instance_method) or [`write_jar_script`](https://rubydoc.brew.sh/Pathname#write_jar_script-instance_method):
```ruby
bin.write_exec_script (libexec/"bin").children
bin.write_exec_script prefix/"Package.app/Contents/MacOS/package"
bin.write_jar_script libexec/jar_file, "jarfile", java_version: "11"
```
* For binaries that require setting one or more environment variables to function properly, use [`write_env_script`](https://rubydoc.brew.sh/Pathname#write_env_script-instance_method) or [`env_script_all_files`](https://rubydoc.brew.sh/Pathname#env_script_all_files-instance_method):
```ruby
(bin/"package").write_env_script libexec/"package", PACKAGE_ROOT: libexec
bin.env_script_all_files(libexec/"bin", PERL5LIB: ENV.fetch("PERL5LIB", nil))
```
### Rewriting a script shebang
Some formulae install executable scripts written in an interpreted language such as Python or Perl. Homebrew provides a `rewrite_shebang` method to rewrite the shebang of a script. This replaces a script's original interpreter path with the one the formula depends on. This guarantees that the correct interpreter is used at execution time. This isn't required if the build system already handles it (e.g. often with `pip` or Perl `ExtUtils::MakeMaker`).
For example, the [`icdiff`](https://github.com/Homebrew/homebrew-core/blob/bc311e91f3a77889e568dec8d3063c3a6cb2965a/Formula/i/icdiff.rb#L18) formula uses this utility. Note that it is necessary to include the utility in the formula; for example with Python one must use `include Language::Python::Shebang`.
### Adding optional steps
**Note:** [`option`](https://rubydoc.brew.sh/Formula#option-class_method)s are not allowed in Homebrew/homebrew-core as they are not tested by CI.
If you want to add an [`option`](https://rubydoc.brew.sh/Formula#option-class_method):
```ruby
class Yourformula < Formula
# ...
url "https://example.com/yourformula-1.0.tar.gz"
sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"
# ...
option "with-ham", "Description of the option"
option "without-spam", "Another description"
depends_on "bar" => :recommended
depends_on "foo" => :optional # automatically adds a with-foo option # automatically adds a without-bar option
# ...
end
```
And then to define the effects the [`option`](https://rubydoc.brew.sh/Formula#option-class_method)s have:
```ruby
if build.with? "ham"
# note, no "with" in the option name (it is added by the build.with? method)
end
if build.without? "ham"
# works as you'd expect. True if `--without-ham` was given.
end
```
[`option`](https://rubydoc.brew.sh/Formula#option-class_method) names should be prefixed with the words `with` or `without`. For example, an option to run a test suite should be named `--with-test` or `--with-check` rather than `--test`, and an option to enable a shared library `--with-shared` rather than `--shared` or `--enable-shared`. See the [alternative `ffmpeg`](https://github.com/homebrew-ffmpeg/homebrew-ffmpeg/blob/HEAD/Formula/ffmpeg.rb) formula for examples.
[`option`](https://rubydoc.brew.sh/Formula#option-class_method)s that aren’t `build.with?` or `build.without?` should be deprecated with [`deprecated_option`](https://rubydoc.brew.sh/Formula#deprecated_option-class_method). See the [`wget`](https://github.com/Homebrew/homebrew-core/blob/3f762b63c6fbbd49191ffdf58574d7e18937d93f/Formula/wget.rb#L27-L31) formula for a historical example.
### Running commands after installation
Any initialization steps that aren't necessarily part of the install process can be located in a `post_install` block, such as setup commands or data directory creation. This block can be re-run separately with `brew postinstall <formula>`.
```ruby
class Foo < Formula
# ...
url "https://example.com/foo-1.0.tar.gz"
def post_install
rm pkgetc/"cert.pem" if File.exist?(pkgetc/"cert.pem")