Composite packages¶
In KnitPkg, packages come in two flavors:
- Single package: self-contained, declares no dependencies.
- Composite package: a package that depends on one or more other packages (directly or indirectly).
A common example is:
@douglasrechia/baris a single package (no dependencies).@douglasrechia/calcis a composite package because it depends on@douglasrechia/bar.
Packages can only depend on packages
A package may depend only on other packages—never on indicators, experts, scripts, libraries, etc.
Direct vs indirect dependencies¶
Dependency graphs quickly become multi-level, so it helps to use precise terms:
- Direct dependency:
calcdepends onbar. (Declared incalcmanifest.) - Indirect dependency:
smadepends oncalc, andcalcdepends onbar, thereforesmadepends indirectly onbar.
As a result of indirect dependencies, KnitPkg builds a dependency resolution tree during kp install.
Key properties of this resolution process:
- The tree contains no circular references.
- The same package will not be resolved twice: once a package is resolved, it is skipped if it appears again later during the recursive traversal.
Practical consequence:
- Each resolved package is materialized only once in the final output:
- either into
knitpkg/include/(include mode), or - into the generated flat header (flat mode).
- either into
Version conflicts and overrides¶
Because packages can appear through multiple paths (indirect dependencies), you may run into a situation like:
- Your project depends on package
A - Your project also depends on package
B Adepends onbar : 1.0.0Bdepends onbar : 2.0.0
KnitPkg will still resolve bar only once. If two different versions would be acceptable depending on the path, the version that wins is:
- the version from the first occurrence of that package during the recursive resolution traversal.
If you need to force a specific version to be used, declare an overrides entry in your manifest to pin that package version.
Single packages¶
Let’s focus on bar as an example of a single package. bar is a package repository that can be consumed by other KnitPkg projects.
As expected for a package, its public code lives under:
knitpkg/include/<organization>/<project_name>/
In the bar repository, this corresponds to:
knitpkg/include/douglasrechia/bar/
Organization naming
The organization part of the path is expected to match the Git repository owner/organization, normalized to lowercase. (Project names do not have to match the repository name, but organizations must.)
Dependencies between headers inside the same package¶
If one header (.mqh) depends on another header within the same package, use a normal MQL #include with a relative path.
| knitpkg/include/douglasrechia/bar/Bar.mqh | |
|---|---|
Here, Bar.mqh includes TimeSeries.mqh using only the filename because both files live in the same directory.
If TimeSeries.mqh were in a subdirectory, you would include it using a relative path, for example:
All package headers must compile
KnitPkg assumes package headers are compiler-clean. If a header forgets to include another header it depends on, the compiler will report missing symbols—and KnitPkg workflows (install/check/install-as-dependency) will not behave correctly.
Composite packages¶
Now let’s focus on calc as an example of a composite package.
Composite packages have to solve a specific problem:
- During development, the author wants IntelliSense and unit tests to compile smoothly.
- When the package is installed as a dependency, the consumer project must receive the real includes pointing at the installed dependency headers.
KnitPkg solves this by combining:
- a development helper include (autocomplete), and
- installation-time directives (
@knitpkg:include) that KnitPkg converts into real#includelines duringkp install.
Cross-package header dependencies¶
The snippet below shows the key idea in Calc.mqh: it includes an autocomplete.mqh file for development, and declares external dependencies via @knitpkg:include.
What to notice:
autocomplete.mqhis a dev-time helper generated bykp autocomplete. It exists to make MetaEditor IntelliSense work and to enable compiling unit tests while developing the package.- The real external dependencies of
Calc.mqhare declared via@knitpkg:includedirectives pointing to headers in other packages (here,bar).
About KnitPkg directives (@knitpkg:*)
KnitPkg directives are written inside /* ... */ comment blocks on purpose. They are not part of standard MQL, so the source file must remain valid MQL.
Important: KnitPkg only recognizes a directive when it is the only thing on the line. If there is anything before the opening /* or anything after the closing */, KnitPkg will not recognize that directive.
During package development, the autocomplete include makes all dependency symbols available so the file compiles cleanly.
But when calc is installed into another project, autocomplete is not supposed to “leak” into the consumer project.
What happens during installation¶
When KnitPkg installs the composite package as a dependency, it:
- disables the autocomplete include, and
- replaces each
@knitpkg:includedirective with a real MQL#includethat points to the installed dependency headers.
The result looks like this:
This is the core mechanism that makes composite packages work reliably:
- Developers keep packages compiler-clean during development (autocomplete helps).
- Consumers get correct dependency includes during installation (
@knitpkg:includebecomes real#include).
Validating directives
When authoring a composite package, you must keep @knitpkg:include directives complete and accurate (every external symbol must be covered). Execute kp checkinstall, which uses the MQL compiler, to validate that installation-time includes are correct.