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 offers two approaches to solve this:


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:

Calc.mqh
#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 #include is 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 by kp autocomplete, which makes all dependency symbols available for IntelliSense and unit test compilation.
  • @knitpkg:include directives written as MQL comments, which KnitPkg converts into real #include lines during kp 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.

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 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: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 ***/

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.