Go library that runs ExifTool in-process: embedded Perl via
zeroperl (Perl compiled to WebAssembly), executed
as native Go via a wasm2go-generated module with the
wasm2go-wasi-host WASI host. You do not need a
system exiftool binary or a separate Perl install.
Upstream ExifTool (Phil Harvey) and metadata: exiftool.org, exiftool/exiftool.
- Self-contained - Perl stdlib, ExifTool, and the interpreter ship inside the module (
embed/). The stdlib is stored LZ4-compressed;perlFS()transparently decompresses files on first read using a 2000-entry LRU cache (lbe/cfsread). - WASI host -
github.com/lbe/wasm2go-wasi-hostimplements WASI snapshot-preview1 for the wasm2go module. Mounts and syscalls are configured throughwasihost.NewModuleConfig()inbuildModuleConfig: writable host directory preopen at/hostfor the caller's working directory (guest cwd/hostvia Perl preamblechdir); read-only FS mounts at/liband/bin; host cwd path aliases;os.TempDir(); and operand parent directories outside cwd/temp for [Command] (see operand_preopen.go). - Same behavior everywhere - one toolchain-based interpreter implementation on Linux, macOS, Windows, etc.
- Trade-off - cold start is heavy (on the order of ~7-10 seconds to initialize the
wasm2go-backed Perl runtime). For many operations, use
NewServerandServer.Commandso ExifTool stays open (-stay_open) and work is amortized.
- Go 1.26+
- Dependencies are listed in
go.mod.
// One-shot: paths are relative to the process working directory; guest cwd is /host.
out, err := exiftool.Command(nil, "-json", "photo.jpg")
// Batch: one Perl startup, many commands.
e, err := exiftool.NewServer("-fast")
if err != nil { /* ... */ }
defer e.Shutdown() // graceful; safe after NewServer returns. Use Close if stopping during init.
out, err = e.Command("-Artist", "photo.jpg")Package docs, API details, and filesystem semantics (mount layout, temp dir, Arg1 / Config): run
go doc -all or open pkg.go.dev.
See TESTING.md and ARCHITECTURE.md. Fast local check (unit only):
go test ./... -count=1Full suite matches CI:
go test -tags=integration,e2e -race ./... -count=1Or: make test-race ARGS="-count=1". Lint: make lint.
The generated zeroperl Go source and Perl install prefix come from
zeroperl. Follow that repository's Build section
(Docker or Apple Container): build the image, run the container, and copy /artifacts into a
host directory (as shown there, e.g. ./output/).
From the build output directory, install these into this repo under embed/:
| Artifact from zeroperl output | Path in go-exiftool |
|---|---|
zeroperl.go |
internal/zeroperl/zeroperl.go |
perl-wasi-prefix/ (entire tree) |
embed/perl-wasi-prefix/ |
Use the generated zeroperl.go artifact unless you intentionally switch runtimes. The ExifTool
script is loaded from embed/perl-wasi-prefix/bin/exiftool in the embedded prefix.
After copying perl-wasi-prefix/ into embed/, run go generate ./... to re-compress the tree
with cfsread-lz4. This updates the LZ4-compressed files in place so the embedded binary reflects
the new Perl build.
zeroperl's README also documents build arguments (PERL_VERSION, EXIFTOOL_VERSION,
BUILD_EXIFTOOL, memory/stack, etc.). The embedded prefix layout (for example lib/perl5 and
lib/perl5/wasm32-wasi) is fixed for a given embed refresh; exiftoolEvalWrapper prepends those
paths to @INC at eval time.
Use dist.pl to download, build, minify, test, and install ExifTool into embed/perl-wasi-prefix/.
perl dist.pl --exiftool-version 13.56Version resolution precedence is:
--exiftool-versionEXIFTOOL_VERSIONenvironment variable- Latest version parsed from
https://exiftool.org/history.html
Command and CommandContext run ExifTool with file paths relative to the process working
directory (or host-absolute paths). The host cwd is preopened at /host; guest cwd is /host.
os.TempDir() and operand parent directories outside cwd/temp are preopened for [Command].
File operands are passed to ExifTool as host-absolute paths. ExifTool can write (or overwrite)
files in the working directory using flags like -overwrite_original or -o <outfile>.
See LICENSE. ExifTool and embedded components carry their own licenses under embed/.