Gumbo.Patches (gumbo v0.3.0)

Copy Markdown View Source

Regenerates the vendored-parser patch series.

Each .patch under c_src/patches/gumbo-parser/ is the build's input: the Makefile copies the pinned source and applies every patch to it, in order. The patches are therefore both the build's input and the record of what each change is, and a generator exists for the ones that are mechanical: it applies the earlier patches, performs a list of literal substitutions, and writes the patch back out as the diff of the pinned source against the result.

That is what mix gumbo.patches does. The substitutions live next to the patch, in a .exs of the same name, and each one asserts how many occurrences it must find: that assertion is the rebase tool, because a pin bump that moves the code fails here — naming the file and the snippet — instead of producing a patch that applies with a different meaning.

The diff itself is Gumbo.Patches.Diff, a port of Python's difflib, so that a regenerated patch is byte-identical to the one the Python generators produced. mix gumbo.patches --check asserts exactly that, and make check-generators runs it in mix ci.

Summary

Functions

Regenerates one patch and compares it with the committed file.

Every definition, sorted by patch name, as {name, definition}.

The generated bytes of patch name: the pinned source with the earlier patches applied, this patch's substitutions run in order, and the diff as difflib renders it, preceded by the definition's header.

The committed patch names, sorted, definitions or not.

Regenerates name and writes it, returning :created when there was no file to compare against, :written when there was one and it differed, and :unchanged when it already says what the definition produces.

Types

definition()

@type definition() :: %{priors: boolean(), header: binary(), subs: [sub()]}

sub()

@type sub() :: %{file: binary(), count: pos_integer(), from: binary(), to: binary()}

Functions

check(name, root \\ File.cwd!())

@spec check(binary(), binary()) :: :ok | {:changed, binary()} | {:missing, binary()}

Regenerates one patch and compares it with the committed file.

Returns :ok, or {:changed, generated} when the bytes differ, or {:missing, generated} when there is no committed file to compare with.

definitions(root \\ File.cwd!())

@spec definitions(binary()) :: [{binary(), definition()}]

Every definition, sorted by patch name, as {name, definition}.

generate(name, root \\ File.cwd!())

@spec generate(binary(), binary()) :: binary()

The generated bytes of patch name: the pinned source with the earlier patches applied, this patch's substitutions run in order, and the diff as difflib renders it, preceded by the definition's header.

patches(root \\ File.cwd!())

@spec patches(binary()) :: [binary()]

The committed patch names, sorted, definitions or not.

write(name, root \\ File.cwd!())

@spec write(binary(), binary()) :: :created | :written | :unchanged

Regenerates name and writes it, returning :created when there was no file to compare against, :written when there was one and it differed, and :unchanged when it already says what the definition produces.

:created is what makes a new patch a one-step thing: a definition with no committed .patch next to it used to have no way through here, and the placeholder that stood in for one was a file the build tried to apply.