Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

235 linhas
8.5KB

  1. .. highlight:: yaml
  2. Library and Package Dependencies
  3. ################################
  4. ``dds`` considers that all libraries belong to a single *package*, but a single
  5. package may contain one or more *libraries*. For this reason, and to better
  6. interoperate with other build and packaging tools, we consider the issues of
  7. package dependencies and library dependencies separately.
  8. .. _deps.pkg-deps:
  9. Package Dependencies
  10. ********************
  11. Consider that we are creating a package ``acme-gadgets@4.3.6``. We declare the
  12. name and version in the ``package.dds`` in the package root:
  13. .. code-block::
  14. Name: acme-gadgets
  15. Version: 4.3.6
  16. Namespace: acme
  17. .. note::
  18. The ``Namespace`` field is required, but will be addressed in the
  19. :ref:`deps.lib-deps` section.
  20. Suppose that our package's libraries build upon the libraries in the
  21. ``acme-widgets`` package, and that we require version ``1.4.3`` or newer, but
  22. not as new as ``2.0.0``. Such a dependency can be declared with the ``Depends``
  23. key:
  24. .. code-block::
  25. :emphasize-lines: 5
  26. Name: acme-gadgets
  27. Version: 4.3.6
  28. Namespace: acme
  29. Depends: acme-widgets ^1.4.3
  30. .. seealso:: :ref:`deps.ranges`.
  31. If we wish to declare additional dependencies, we simply declare them with
  32. additional ``Depends`` keys
  33. .. code-block::
  34. :emphasize-lines: 5-7
  35. Name: acme-gadgets
  36. Version: 4.3.6
  37. Namespace: acme
  38. Depends: acme-widgets ^1.4.3
  39. Depends: acme-gizmos ~5.6.5
  40. Depends: acme-utils ^3.3.0
  41. When ``dds`` attempts to build a project, it will first build the dependency
  42. solution by iteratively scanning the dependencies of the containing project and
  43. all transitive dependencies.
  44. .. _deps.ranges:
  45. Compatible Range Specifiers
  46. ===========================
  47. When specifying a dependency on a package, one will want to specify which
  48. versions of the dependency are supported.
  49. .. note::
  50. Unlike other packaging tools, ``dds`` will find a solution with the
  51. *lowest* possible version that satisfies the given requirements for each
  52. package. This decision is not incidental: It's entirely intentional.
  53. Refer to: :ref:`deps.ranges.why-lowest`.
  54. ``dds`` compatible-version ranges are similar to the shorthand range specifiers
  55. supported by ``npm`` and ``npm``-like tools. There are five (and a half)
  56. version range formats available, listed in order of most-to-least restrictive:
  57. Exact: ``1.2.3`` and ``=1.2.3``
  58. Specifies an *exact* requirement. The dependency must match the named
  59. version *exactly* or it is considered incompatible.
  60. Minor: ``~1.2.3``
  61. Specifies a *minor* requirement. The version of the dependency should be
  62. *at least* the given version, but not as new or newer than the next minor
  63. revision. In this example, it represents the half-open version range
  64. ``[1.2.3, 1.3.0)``.
  65. Major: ``^1.2.3``
  66. Specifies a *major* requirement. The version must be *at least* the same
  67. given version, but not any newer than the the next major version. In the
  68. example, this is the half-open range ``[1.2.3, 2.0.0)``.
  69. .. note::
  70. This is the recommended default option to reach for, as it matches the
  71. intended behavior of `Semantic Versioning <https://semver.org>`_.
  72. At-least: ``+1.2.3``
  73. Specifies an *at least* requirement. The version must be *at least* the
  74. given version, but any newer version is acceptable.
  75. Anything: ``*``
  76. An asterisk ``*`` represents than *any* version is acceptable. This is not
  77. recommended for most dependencies.
  78. .. _deps.ranges.why-lowest:
  79. Why Pull the *Lowest* Matching Version?
  80. ---------------------------------------
  81. When resolving dependencies, ``dds`` will pull the version of the dependency
  82. that is the lowest version that satisfies the given range. In most cases,
  83. this will be the same version that is the base of the version range.
  84. Imagine a scenario where we *did* select the "latest-matching-version":
  85. Suppose we are developing a library ``Gadgets``, and we wish to make use of
  86. ``Widgets``. The latest version is ``1.5.2``, and they promise Semantic
  87. Versioning compatibility, so we select a version range of ``^1.5.2``.
  88. Suppose a month passes, and ``Widgets@1.6.0`` is published. A few things
  89. happen:
  90. #. Our CI builds now switch from ``1.5.2`` to ``1.6.0`` *without any code
  91. changes*. Should be okay, right? I mean... it's still compatible, yeah?
  92. #. Bugs in ``Widgets@1.6.0`` will now appear in all CI builds, and won't be
  93. reproducible locally unless we re-pull our dependencies and obtain the
  94. new version of ``Widgets``. This requires that we be conscientious enough to
  95. realize what is actually going on.
  96. #. Even if ``Widgets@1.6.0`` introduces no new bugs, a developer re-pulling
  97. their dependencies will suddenly be developing against ``1.6.0``, and may
  98. not even realize it. In fact, this may continue for weeks or months until
  99. *everyone* is developing against ``1.6.0`` without realizing that they
  100. actually only require ``1.5.2`` in their dependency declarations.
  101. #. Code in our project is written that presupposes features or bugfixes added
  102. in ``1.6.0``, and thus makes the dependency declaration on ``Widgets ^1.5.2``
  103. a *lie*.
  104. Pulling the lowest-matching-version has two *huge* benefits:
  105. #. No automatic CI upgrades. The code built today will produce the same result
  106. when built a year from now.
  107. #. Using a feature/fix beyond our minimum requirement becomes a compile error,
  108. and we catch these up-front rather than waiting for a downstream user
  109. discovering them for us.
  110. *Isn't this what lockfiles are for?*
  111. """"""""""""""""""""""""""""""""""""
  112. Somewhat. Lockfiles will prevent automatic upgrades, but they will do nothing
  113. to stop accidental reliance on new versions. There are other useful features
  114. of lockfiles, but preventing automatic upgrades can be a non-issue by simply
  115. using lowest-matching-version.
  116. *So, if this is the case, why use ranges at all?*
  117. """""""""""""""""""""""""""""""""""""""""""""""""
  118. In short: *Your* compatibility ranges are not for *you*. They are for *your
  119. users*.
  120. Suppose package ``A`` requires ``B ^1.0.0``, and ``B`` requires ``C ^1.2.0``.
  121. Now let us suppose that ``A`` wishes to use a newer feature of ``C``, and thus
  122. declares a dependency on ``C ^1.3.0``. ``B`` and ``A`` have different
  123. compatibility ranges on ``C``, but this will work perfectly fine **as long as
  124. the compatible version ranges of A and B have some overlap**.
  125. That final qualification is the reason we use compatibility ranges: To support
  126. our downstream users to form dependency graphs that would otherwise form
  127. conflicts if we required *exact* versions for everything. In the above example,
  128. ``C@1.3.0`` will be selected for the build of ``A``.
  129. Now, if another downstream user wants to use ``A``, they will get ``C@1.3.0``.
  130. But they discover that they actually need a bugfix in ``C``, so they place
  131. their own requirement on ``C ^1.3.1``. Thus, they get ``C@1.3.1``, which still
  132. satisfies the compatibility ranges of ``A`` and ``B``. Everyone gets along
  133. just fine!
  134. .. _deps.lib-deps:
  135. Library Dependencies
  136. ********************
  137. In ``dds``, library interdependencies are tracked separately from the packages
  138. that contain them. A library must declare its intent to use another library
  139. in the ``library.dds`` at its library root. The minimal content of a
  140. ``library.dds`` is the ``Name`` key:
  141. .. code-block::
  142. Name: gadgets
  143. To announce that a library wishes to *use* another library, use the aptly-named
  144. ``Uses`` key:
  145. .. code-block::
  146. :emphasize-lines: 3-5
  147. Name: gadgets
  148. Uses: acme/widgets
  149. Uses: acme/gizmos
  150. Uses: acme/utils
  151. Here is where the package's ``Namespace`` key comes into play: A library's
  152. qualified name is specified by joining the ``Namespace`` of the containing
  153. package with the ``Name`` of the library within that package with a ``/``
  154. between them.
  155. It is the responsibility of package authors to document the ``Namespace`` and
  156. ``Name`` of the packages and libraries that they distribute.
  157. .. note::
  158. The ``Namespace`` of a package is completely arbitrary, and need not relate
  159. to a C++ ``namespace``.
  160. .. note::
  161. The ``Namespace`` need not be unique to a single package. For example, a
  162. single organization (Like Acme Inc.) can share a single ``Namespace`` for
  163. many of their packages and libraries.
  164. However, it is essential that the ``<Namespace>/<Name>`` pair be
  165. universally unique, so choose wisely!
  166. Once the ``Uses`` key appears in the ``library.dds`` file of a library, ``dds``
  167. will make available the headers for the library being used, and will
  168. transitively propagate that usage requirement to users of the library.