Skip to main content

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

PropertyTypeRequiredDescription
sourcePathstringyesPath to a file or directory on your disk. Relative paths are resolved against the working directory.
targetPathstringyesAbsolute 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 App or 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

ValidatorWhat it catches
requiredsourcePath and targetPath cannot be missing or null.
valid path on disksourcePath must exist on disk at build time.
valid absolute pathtargetPath must be a valid absolute path with no illegal characters.
minNeither path may be empty.

Common error messages – see Validation.