Files and Folders
This is the most important capability of any installer: putting files on disk. In MPDEV that is a single fileSystemEntries array of { sourcePath, targetPath } pairs.
Applies to: MSI and MSIX.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
sourcePath | string | yes | Path to a file or directory on your disk. Relative paths are resolved against the working directory. |
targetPath | string | yes | Absolute path inside the installed package (where the file ends up after install). |
The four behaviors of fileSystemEntries
The semantics are determined by what sourcePath points to and how targetPath is written.
1. Source = directory → target is a directory (recursive copy)
{ "sourcePath": "source",
"targetPath": "$.installDir" }
source/is scanned recursively.- Sub-directories and file names are preserved.
- Everything ends up inside
$.installDir.
If source/ looks like:
source/
├── MyApp.exe
└── docs/
└── README.txt
Then after install you get:
$.installDir/MyApp.exe
$.installDir/docs/README.txt
2. Source = file → target is a file (rename / move)
{ "sourcePath": "source\\MyApp.exe",
"targetPath": "$.installDir\\MyApp.exe" }
The single file is copied to the exact target path. Use this to rename, relocate, or replace a single file relative to a recursive directory entry.
3. Source = file → target is a directory (auto-append filename)
If targetPath ends with \, MPDEV treats it as a directory and appends the source file name automatically.
{ "sourcePath": "source\\MyApp.exe",
"targetPath": "$.installDir\\bin\\" }
Result on disk: $.installDir\bin\MyApp.exe.
4. Concrete declarations override recursive ones
If you declare a directory recursively AND a specific file from inside that directory, the specific entry wins for that file. This is how you relocate or rename a single file in an otherwise recursive copy.
"fileSystemEntries": [
{ "sourcePath": "source",
"targetPath": "$.installDir" },
{ "sourcePath": "source\\third-party\\old-name.dll",
"targetPath": "$.installDir\\bin\\new-name.dll" }
]
old-name.dll is moved to $.installDir\bin\new-name.dll. Every other file under source/ lands in its mirrored location under $.installDir.
Source paths
- May be relative (resolved against
--working-dir, defaulting to the current directory) or absolute. - Must point to something that exists on disk at build time. Validation fails otherwise (
File not found). - Forward slashes work but conventional Windows backslashes (
\\in JSON strings) are clearer. - Environment variables are expanded (
%BUILD_OUTPUT%\\bin).
Target paths
- Must be absolute. Use env vars like
%ProgramFiles%\\My Appor property references like$.installDir\\bin. - Cannot contain illegal Windows path characters (
< > : " | ? *). - May reference any property in the package (
$.packageName,$.version, etc.). - Keep paths under 256 characters where possible.
- MSIX virtualization: targets like
%WINDIR%\Fonts\…are valid for MSI but become package-relative under MSIX. Test both outputs if you build both. - Hidden, system, and read-only attributes on source files are preserved. Strip them in your build pipeline if you do not want them shipped.
No exclude filters – on purpose
There is intentionally no exclude or glob mechanism. The MPDEV philosophy is:
Ship the same files you tested with.
If you need to exclude things, do so before invoking mpdev:
- Have your build pipeline output a clean directory.
- Use a post-build step to delete unwanted files.
- Or, declare every file individually instead of using a recursive directory entry.
Examples
Ship a build output, plus a few "installer-only" files
"fileSystemEntries": [
{ "sourcePath": "build\\Release",
"targetPath": "$.installDir" },
{ "sourcePath": "installer-assets\\eula.rtf",
"targetPath": "$.installDir\\eula.rtf" },
{ "sourcePath": "installer-assets\\third-party-licenses.txt",
"targetPath": "$.installDir\\licenses\\THIRD-PARTY.txt" }
]
Place files outside installDir
You can install anywhere – use absolute paths or env vars:
"fileSystemEntries": [
{ "sourcePath": "fonts\\MyCompanyFont.ttf",
"targetPath": "%WINDIR%\\Fonts\\MyCompanyFont.ttf" },
{ "sourcePath": "templates\\default.json",
"targetPath": "%ProgramData%\\My Application\\templates\\default.json" }
]
MSI gets one file, MSIX another
Use the per-output sections to add files only to one build.
{
"fileSystemEntries": [
{ "sourcePath": "source", "targetPath": "$.installDir" }
],
"msi": {
"fileSystemEntries": [
{ "sourcePath": "extras\\msi-only-helper.exe",
"targetPath": "$.installDir\\helper.exe" }
]
},
"msix": {
"fileSystemEntries": [
{ "sourcePath": "extras\\msix-only-readme.txt",
"targetPath": "$.installDir\\readme.txt" }
]
}
}
The msi and msix arrays are appended to the common one for the corresponding output.
Validation rules
| Validator | What it catches |
|---|---|
required | sourcePath and targetPath cannot be missing or null. |
valid path on disk | sourcePath must exist on disk at build time. |
valid absolute path | targetPath must be a valid absolute path with no illegal characters. |
min | Neither path may be empty. |
Common error messages – see Validation.