Skip to content

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:

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: calc depends on bar. (Declared in calc manifest.)
  • Indirect dependency: sma depends on calc, and calc depends on bar, therefore sma depends indirectly on bar.

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).

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
  • A depends on bar : 1.0.0
  • B depends on bar : 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.

Example:

knitpkg/include/douglasrechia/bar/Bar.mqh
#include "TimeSeries.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:

#include "series/TimeSeries.mqh"

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 #include lines during kp 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.

Calc.mqh
//------------------------------------------------------------------
// Development autocomplete — resolves dependencies and enables
// MetaEditor IntelliSense; automatically neutralized by KnitPkg
// installer.
// Run `kp autocomplete` to regenerate.
//------------------------------------------------------------------
#include "../../../autocomplete/autocomplete.mqh"


//------------------------------------------------------------------
// KnitPkg include directives — used by KnitPkg installer at the time
// this package is installed as a dependency into another KnitPkg
// project.
//------------------------------------------------------------------
/* @knitpkg:include "douglasrechia/bar/TimeSeries.mqh" */
/* @knitpkg:include "douglasrechia/bar/Bar.mqh" */

What to notice:

  • autocomplete.mqh is a dev-time helper generated by kp autocomplete. It exists to make MetaEditor IntelliSense work and to enable compiling unit tests while developing the package.
  • The real external dependencies of Calc.mqh are declared via @knitpkg:include directives 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:include directive with a real MQL #include that points to the installed dependency headers.

The result looks like this:

knitpkg/include/douglasrechia/calc/Calc.mqh installed in `calclibimp`
//------------------------------------------------------------------
// Development autocomplete — resolves dependencies and enables
// MetaEditor IntelliSense; automatically neutralized by KnitPkg
// installer.
// Run `kp autocomplete` to regenerate.
//------------------------------------------------------------------
// #include "../../../autocomplete/autocomplete.mqh"  /*** ← disabled by KnitPkg install (dev helper) ***/


//------------------------------------------------------------------
// KnitPkg include directives — used by KnitPkg installer at the time
// this package is installed as a dependency into another KnitPkg
// project.
//------------------------------------------------------------------
#include "../bar/TimeSeries.mqh" /*** ← dependency added by KnitPkg ***/
#include "../bar/Bar.mqh" /*** ← dependency added by KnitPkg ***/

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:include becomes 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.