"Separate compiled code from source file" moves a VI's compiled code out of the .vi file into a local object cache, so recompiling a caller because its callee changed no longer rewrites the caller on disk. That removes most of the spurious "modified" files Git shows after a small LabVIEW change. It doesn't make VIs diffable, and a mass compile still touches every file.
What's Inside a VI File
By default a .vi holds three things in one binary file: the front panel, the block diagram, and the compiled code for the platform and LabVIEW version that last saved it. The source and the machine code travel together.
That coupling is what produces the noise. When subVI B changes, LabVIEW recompiles every caller A that's loaded, because A's compiled code depends on B's connector pane and inlining. A is now modified in memory, and saving it rewrites A's bytes even though nobody opened A's diagram.
Git can't tell the difference. It sees forty changed files after a one-VI edit, and the commit history records a change to A that never happened in A's source.
What the Setting Changes
The setting has existed since LabVIEW 2010, per VI and per project. Per VI it's in VI Properties, General page. Per project it's in the project properties, where "Separate compiled code from new project files" applies to files added from then on, and a "Mark Existing Items" button applies it to everything already in the project.
With it on, LabVIEW saves only the source in the .vi and writes the compiled code to the VI object cache. The cache is a per-user, per-LabVIEW-version, per-bitness directory on the local machine, with its location set under Tools, Options, Environment. It's never in the repository, so a fresh clone opens slower the first time while the cache fills, and a build machine rebuilds it from scratch.
Built executables are unaffected. The Application Builder still embeds compiled code in the executable, so nothing changes for the station.
The two NI pages that describe the mechanism are separating compiled code from VIs and other file types and separating compiled code for source control.
What Improves in Git
The gain is that "modified" starts to mean "the source changed". Here is what Git shows for the common cases, before and after.
| Change | Before, compiled code inside the VI | After, compiled code in the cache |
|---|---|---|
| Callee B's diagram edited | B and every loaded caller A show as modified | Only B |
| Caller A's diagram edited | A, plus anything A's change forces to recompile up the chain | Only A |
| Mass compile after a LabVIEW version upgrade | Every VI rewritten | Every VI still rewritten, the source format changes with the version |
| Bitness change, 32-bit to 64-bit | Every VI recompiled and saved | Compiled code goes to a separate cache, saved files still show as touched |
| A LabVIEW class after a mass compile | .lvclass and members modified | .lvclass can still show as modified |
Read the first two rows as the win. A commit after a limit change in one subVI contains one file, which makes the history readable and a revert safe. The reason that matters, and everything else that binary files cost a team, is in why LabVIEW version control is so painful.
Read the last three rows as the limit. A version upgrade, a bitness change or a full mass compile still rewrites the tree, and the history of individual changes on either side of that commit no longer diffs cleanly. Upgrade a station from LabVIEW 2019 to 2024 plans around that commit rather than pretending it isn't there.
What Doesn't Change
The .vi is still binary. Git still says "Binary files differ", git blame still names one author for the whole file, and a pull request still shows nothing a reviewer can read.
Merging is unchanged too. Two people editing the same VI on two branches still need LVMerge, which needs the Professional Development System and the same LabVIEW version on the machine doing the merge. The setting reduces how often two people touch the same file by accident, which helps, but a real conflict is still resolved as fix LabVIEW merge conflicts on binary VIs describes.
The setting is a source control hygiene fix. It's worth doing, and it's the whole of what it does.
How to Turn It On for a Project Safely
The switch touches every file once. Do it in one sitting so the noise lands in one commit.
- Commit a clean tree and tag it, so there's a known point before the change.
- Put everyone on the same LabVIEW version and bitness. The cache is per version and per bitness anyway, and mixing them during the switch produces a second round of modified files.
- In the project properties, tick "Separate compiled code from new project files", then use "Mark Existing Items" to apply the setting to every VI, control, class and library already in the project.
- Mass compile the project once, from Tools, Advanced, Mass Compile, so every file is saved with the new setting and a valid cache entry.
- Commit everything as one commit with a message that says what it is, and tag it.
- Tell Git the files are binary, so it never tries to diff or merge them as text.
# LabVIEW source is binary: never line-diff or auto-merge these.*.vi binary*.vit binary*.ctl binary*.ctt binaryFrom the next commit on, a modified VI means someone edited it. A colleague who pulls the tagged commit opens the project, waits for the cache to fill, and sees a clean tree.
The one-time commit is large and useless to review. Name it clearly, then treat it as a boundary: git log before it and after it are both readable, across it they aren't.
The Classes Caveat
A .lvclass file is XML, but it carries the class's private data control flattened inside it. A mass compile can re-flatten that control and change the bytes, so a class shows as modified after a mass compile even with separate compiled code on and no source change anywhere.
Expect it, and don't chase it. After a mass compile, commit the classes with everything else and don't try to review those diffs. During normal work, a class that shows as modified when nobody edited it is a sign that a member VI or the private data control was recompiled, and it's safe to commit or discard.
The Contrast: A Repository With No Compiled Code At All
A Python station under the TofuPilot Framework has nothing to separate. The repository holds procedure.yaml, phases/*.py and plugs/*.py, all text, and Git diffs, blames and merges them with no plugin and no licence.
The compiled artifact exists, but it lives somewhere else. A Git push creates a deployment in TofuPilot, an immutable build of the procedure at that commit with an id like dep_abc123, and the station fetches it with tofupilot pull. The repository only ever holds source, every run uploaded from the station carries the deployment that produced it, and a mass compile isn't a concept.
Start With One Station
When you do move a station, pick the one where the mass compile commit hurt most, rebuild it in Python with the TofuPilot Framework, and run it next to the LabVIEW version on the same units for two weeks. The repository for that station will hold source only.
# Install the CLI, then run the station locally (no account needed) or with upload.curl -fsSL https://www.tofupilot.app/install | shtofupilot run ./procedure.yamltofupilot run ./procedure.yaml --uploadThe rebuild is in how to migrate from LabVIEW to Python for manufacturing tests with TofuPilot, and the framework is at tofupilot.com/products/framework. The Lab tier is free, and tofupilot run works without an account.
