Installation

Hack Codegen is installed with with Composer:

1
$ composer require facebook/hack-codegen

Commit Your Codegen Output

In other languages, it’s usually best practice not to commit generated artifacts; this isn’t the case for generated Hack code, as not commiting the output creates a circular dependency:

  • If the Hack typechecker reports any errors, HHVM raises a fatal error when any Hack code is executed
  • References to undefined classes, functions, types, etc are a typechecker error
  • Any references to your generated code will be undefined references on a fresh checkout
  • As Hack Codegen itself is written in Hack, a fatal error will be raised by HHVM when you try to build your code.

Additionally, this is required to support partially-generated files.

A Simple Example

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
<?hh
require_once('vendor/autoload.php');

use Facebook\HackCodegen\{
  CodegenFileType,
  HackCodegenConfig,
  HackCodegenFactory,
  HackBuilderValues
};

function make_script(): void {
  $cg = new HackCodegenFactory(
    new HackCodegenConfig(__DIR__),
  );

  $cg->codegenFile('script.php')
    ->setFileType(CodegenFileType::HACK_PARTIAL)
    ->setShebangLine('#!/usr/bin/env hhvm')
    ->setPseudoMainHeader(
      'require_once("vendor/autoload.php");',
    )
    ->addFunction(
      $cg->codegenFunction('main')
        ->setReturnType('void')
        ->setBody(
          $cg->codegenHackBuilder()
            ->startManualSection('greeting')
            ->addAssignment(
              '$greeting',
              "Hello, world!",
              HackBuilderValues::export(),
            )
            ->endManualSection()
            ->addLine(
              'printf("%s\n", $greeting);'
            )
            ->getCode(),
        ),
    )
    ->setPseudoMainFooter(
      'main();',
    )
    ->save();
}

make_script();

This generates:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env hhvm
<?hh
/**
 * This file is partially generated. Only make modifications between BEGIN
 * MANUAL SECTION and END MANUAL SECTION designators.
 *
 * @partially-generated SignedSource<<484789e339a716fbb2ed2385d32b6b75>>
 */
require_once("vendor/autoload.php");

function main(): void {
  /* BEGIN MANUAL SECTION greeting */
  $greeting = 'Hello, world!';
  /* END MANUAL SECTION */
  printf("%s\n", $greeting);
}

main();

Changes can be made between the MANUAL SECTION markers, and will be preserved regardless of any codegen changes to the rest of the file.

Edit on GitHub