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 offers two approaches to solve this:
Option 1: @knitpkg:wired (v1.1.0+, recommended)¶
Introduced in KnitPkg v1.1.0, the @knitpkg:wired directive is the recommended approach. Dependencies are declared as normal MQL #include statements pointing to the autocomplete folder, marked with the @knitpkg:wired annotation:
#include "../../../autocomplete/knitpkg/include/douglasrechia/bar/TimeSeries.mqh" /* @knitpkg:wired */
#include "../../../autocomplete/knitpkg/include/douglasrechia/bar/Bar.mqh" /* @knitpkg:wired */
This approach is natural to MQL developers because:
- The
#includeis a real MQL include — MetaEditor resolves it directly, no autocomplete.mqh helper needed. - Only the specific headers you need are included, avoiding namespace pollution from unrelated symbols.
- The code reads like standard MQL, making it easier to understand and maintain.
During kp install, KnitPkg detects the @knitpkg:wired annotation and replaces the autocomplete path with the real installed header path.
About @knitpkg:wired
The /* @knitpkg:wired */ annotation must be on the same line as the #include. The include path must point to a header inside the knitpkg/autocomplete/knitpkg/include/ directory — those headers are generated when you run kp autocomplete.
Option 2: autocomplete.mqh + @knitpkg:include¶
The classic approach combines:
- a development helper include (
autocomplete.mqh), generated bykp autocomplete, which makes all dependency symbols available for IntelliSense and unit test compilation. @knitpkg:includedirectives written as MQL comments, which KnitPkg converts into real#includelines duringkp install.
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 directives are written inside /* ... */ comment blocks on purpose. They are not part of standard MQL, so the source file must remain valid MQL.
Applicable to @knitpkg:include only: KnitPkg only recognizes @knitpkg:include 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. 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:
The downside of this approach is that autocomplete.mqh pulls in all symbols from all dependencies into the namespace, which can pollute IntelliSense with unrelated names.
Validating directives
Regardless of which approach you use, run kp checkinstall to validate that all installation-time includes are correct before publishing.