First, you build a packed project library (PPL) from source. Then, you write a VI that calls that PPL. It works fine. But now you load the caller VI under a different target in your project. The caller VI breaks because it tries to load the PPL, and the PPL refuses because it isn't built for the new target.
Packed project libraries are compiled for one and only one specific target.
How can you write ONE caller VI that will load DIFFERENT libraries depending upon the target?
Solution Summary and Caveats
A VI saves the paths to its dependencies either as absolute paths or relative paths. When the paths point into the core directories of LabVIEW, the VI saves a "pseudopath", also known as a "symbolic path". LabVIEW can resolve those pseudopaths differently for each target. By installing different builds of the same library into different directories, we can make the same caller VI load different dependencies per target.
Caveat 1. This documentation applies to LabVIEW 8.0 through LabVIEW 2020 but is subject to change in a future version of LabVIEW. Because the feature was never intended for public consumption, there are parts of the design that were just “good enough.” If we make this part of official documentation, we may change some of the specific directory paths. But the general pattern laid out here should be stable enough for you to begin building production systems on top of it.
Caveat 2. This document will focus on "vi.lib" only. LabVIEW supports other pseudopaths, but the other pseudopaths generally do not support this per-target resolution. The support is either non-existent or very use-case specific (i.e. loading .mnu files for the palettes). This is one of the areas that we may expand in the future as we reconsider the utility of this feature.
Pseudopaths are the symbolic path names that a VI saves for its dependencies when those dependencies are located inside the LabVIEW installation. Normally, these are resolved at load time based solely on the LabVIEW version number, bitness, and OS platform.
For example, if you use 64-bit LabVIEW 2019 on Windows and write a caller VI invokes the “Clear Errors.vi” that ships with LabVIEW, the subVI is located at
C:\Program Files\National Instruments\LabVIEW 2019\vi.lib\Utility\error.llb\Clear Errors.vi
But the caller VI will save
<vilib>:\ Utility\error.llb\Clear Errors.vi
When loading, that pseudopath will be detected and resolved. So, if you load the VI on 32-bit LV 2020 on Windows, the path would be resolved to
C:\Program Files (x86)\National Instruments\LabVIEW 2020\vi.lib\Utility\error.llb\Clear Errors.vi
But if you loaded on LabVIEW 2020 for Mac, the path would resolve to
/Applications/National Instruments/LabVIEW 2020 64-bit/vi.lib/Utility/error.llb/Clear Errors.vi
Resolving Pseudopaths Per Project Target
Since LabVIEW 8.0 (2005), LabVIEW has had the ability to resolve pseudopaths differently depending upon which target the caller VI is under in the project tree. This allowed NI to ship different definitions for specific VIs depending upon the target. Nowadays, NI mostly only uses this feature for IP blocks on FPGA targets and for .mnu files to give targets specific palette menus. For source VIs, the twin features of “source only" and "conditional disable structure” allow us to write one VI that can compile different ways based on targets.
But for users who want to distribute already-built PPLs that are (by definition) compiled for a specific target, they need a way to supply one .lvlibp file compiled for desktop and a different version of the same .lvlibp compiled for PXI, cRIO, etc. The ability to resolve the pseudopaths differently based on targets exactly solves this problem. Users can simultaneously install of multiple versions of the same dependency.
The following table gives the paths that you can use for specific targets. The [labview] directory means “whichever directory your LabVIEW is installed into”, which will vary based on version, bitness, and OS platform.
This table is incomplete. It is only a sample of the supported targets. See comments below.
Pseudopath resolution of <vilib>:\x.vi *
PXI running Linux
PXI running Parlap
PXI running VxWorks
cRIO running Linux**
cRIO running VxWorks
All ARM cRIOs***
It gets complicated.
* In general, the path for any RT target can be formed by starting with “[labview]\Targets\NI\” and then appending the target classification, then appending the OS, then appending the string for the form factor. The classifications include “RT”, “FPGA”, or “NXT”. NXT is the LEGO NXT brick target. There are other classifications no longer in use. LINX targets are in one place in LV2020 but will likely move in future release, and so are skipped for now.
** Why cRIO was given the string “EmbeddedUI” is a mystery lost to the ages. It has absolutely nothing to do with the option "Allow development for this target with embedded UI enabled" found in Target Properties dialog.
*** Notice that ARM does not have a form-factor specific target underneath the OS. Support for this redirect was never added. Please read about the impact of this in “Directory Chaining” below.
**** Figuring out the string for a specific FPGA target is tricky. The paths can be arbitrarily deep in a forking tree of types of FPGAs. Each type of target in the project tree will map to a directory containing a “resource.xml” file. You may be able to search out these files in LV’s install directory and identify which one applies to your target. Since PPLs do not work on FPGA, this document will not go into further detail.
Each target does not just check a single subdirectory for a file. It checks a chain of directories until it finds one that contains the desired file. It drops each of the qualifiers in order as it goes up, until eventually it switches over to the standard vi.lib location.
For example, a search for <vilib>:\x.vi on Linux cRIO will wind up checking the following locations in order:
This chaining allows you to have one file installed that covers several targets. This may be minimally useful for PPLs, but does have value for raw VIs and .mnu files.
The fact that ARM targets were never assigned a specific form-factor subdirectory means that any code specific for them will go in the “all targets of that class and OS” directory. You will then have to provide specific overrides for all the other targets, otherwise they will all end up using ARM’s code.
Translating Pseudopaths to Real Paths
If you have a pseudopath and need to know its translation to a path on disk, use this VI:
resource\Framework\Providers\MessageRescripter\Support\Resolve Symbolic Path.vi
This VI only returns the deepest resolution of the pseudopath, not the complete chain of directories. It can be helpful to figure out the directory for a specific target. If you want to include this VI in your code, please make a copy of it rather than linking directly to the “resource” directory.