OLD | NEW |
| (Empty) |
1 <ol class="toc"> | |
2 <li><a href="#a-name-and-a-number">A name and a number</a></li> | |
3 <li><a href="#shared-dependencies-and-unshared-libraries">Shared dependencies
and unshared libraries</a></li> | |
4 <li><a href="#version-lock">Version lock</a></li> | |
5 <li><a href="#version-constraints">Version constraints</a></li> | |
6 <li><a href="#semantic-versions">Semantic versions</a></li> | |
7 <li><a href="#constraint-solving">Constraint solving</a></li> | |
8 <li><a href="#constraint-context">Constraint context</a></li> | |
9 <li><a href="#lockfiles">Lockfiles</a></li> | |
10 <li><a href="#when-things-go-wrong">When things go wrong</a></li> | |
11 <li><a href="#summary">Summary</a></li> | |
12 </ol> | |
13 | |
14 <p>One of pub’s main jobs is helping you work with versioning. Here, I&rsq
uo;ll | |
15 explain a bit about the history of versioning and pub’s approach to it. | |
16 Consider this to be advanced information. If you want a better picture of <em>wh
y</em> | |
17 pub was designed the way it was, read on. If you just want to <em>use</em> pub,
the | |
18 <a href="index.html">other docs</a> will serve you better.</p> | |
19 | |
20 <p>Modern software development, especially web development, leans heavily on | |
21 reusing lots and lots of existing code. That includes code <em>you</em> wrote in
the | |
22 past, but also stuff from third-parties, everything from big frameworks to tiny | |
23 little utility libraries. It’s not uncommon for an application to depend o
n | |
24 dozens of different packages and libraries.</p> | |
25 | |
26 <p>It’s hard to understate how awesome this is. When you see stories of ti
ny web | |
27 startups building a site in a few weeks that gets millions of users, the | |
28 only reason they can pull that off is because the open source community has | |
29 laid a feast of software at their feet.</p> | |
30 | |
31 <p>But there’s still no such thing as a free lunch. There’s a challe
nge to code | |
32 reuse, especially reusing code you don’t maintain. When your app uses tons
of | |
33 code being developed by other people, what happens when they change it? They | |
34 don’t want to break your app, and you certainly don’t either.</p> | |
35 | |
36 <h2 id="a-name-and-a-number">A name and a number</h2> | |
37 | |
38 <p>We solve this by <em>versioning</em>. When you depend on some piece of outsid
e code, | |
39 you don’t just say “My app uses <code>widgets</code>.” You say
, “My app uses | |
40 <code>widgets 2.0.5</code>.” That combination of name and version number u
niquely | |
41 identifies an <em>immutable</em> chunk of code. The people hacking on <code>widg
ets</code> can | |
42 make all of the changes they want, but they promise to not touch any already | |
43 released versions. They can put out <code>2.0.6</code> or <code>3.0.0</code> and
it won’t affect you | |
44 one whit because the version you use is unchanged.</p> | |
45 | |
46 <p>When you <em>do</em> want to get those changes, you can always point your app
to a | |
47 newer version of <code>widgets</code> and you don’t have to coordinate wit
h those | |
48 developers to do it. So, problem solved, right?</p> | |
49 | |
50 <h2 id="shared-dependencies-and-unshared-libraries">Shared dependencies and unsh
ared libraries</h2> | |
51 | |
52 <p>Well, no. Depending on specific versions works fine when your dependency <em>
graph</em> | |
53 is really just a dependency <em>tree</em>. If your app depends on a bunch of stu
ff, and | |
54 those things in turn have their own dependencies and so on, that all works fine | |
55 as long as none of those dependencies <em>overlap</em>.</p> | |
56 | |
57 <p>But let’s consider an example:</p> | |
58 | |
59 <pre><code> myapp | |
60 / \ | |
61 / \ | |
62 widgets templates | |
63 \ / | |
64 \ / | |
65 collections | |
66 </code></pre> | |
67 | |
68 <p>So your app uses <code>widgets</code> and <code>templates</code>, and <em>bot
h</em> of those use | |
69 <code>collections</code>. This is called a <strong>shared dependency</strong>. N
ow what happens when | |
70 <code>widgets</code> wants to use <code>collections 2.3.5</code> and <code>templ
ates</code> wants | |
71 <code>collections 2.3.7</code>? What if they don’t agree on a version?</p> | |
72 | |
73 <p>One option is to just let the app use both | |
74 versions of <code>collections</code>. It will have two copies of the library at
different | |
75 versions and <code>widgets</code> and <code>templates</code> will each get the o
ne they want.</p> | |
76 | |
77 <p>This is what <a href="https://npmjs.org/">npm</a> does for node.js. Would it
work for Dart? Consider this | |
78 scenario:</p> | |
79 | |
80 <ol> | |
81 <li><code>collections</code> defines some <code>Dictionary</code> class.</li> | |
82 <li><code>widgets</code> gets an instance of it from its copy of <code>collect
ions</code> (<code>2.3.5</code>). | |
83 It then passes it up to <code>myapp</code>.</li> | |
84 <li><code>myapp</code> sends the dictionary over to <code>templates</code>.</l
i> | |
85 <li>That in turn sends it down to <em>its</em> version of <code>collections</c
ode> (<code>2.3.7</code>).</li> | |
86 <li>The method that takes it has a <code>Dictionary</code> type annotation for
that object.</li> | |
87 </ol> | |
88 | |
89 <p>As far as Dart is concerned, <code>collections 2.3.5</code> and <code>collect
ions 2.3.7</code> are | |
90 entirely unrelated libraries. If you take an instance of class <code>Dictionary<
/code> from | |
91 one and pass it to a method in the other, that’s a completely different | |
92 <code>Dictionary</code> type. That means it will fail to match a <code>Dictionar
y</code> type | |
93 annotation in the receiving library. Oops.</p> | |
94 | |
95 <p>Because of this (and because of the headaches of trying to debug an app that | |
96 has multiple versions of things with the same name), we’ve decided npm&rsq
uo;s model | |
97 isn’t a good fit.</p> | |
98 | |
99 <h2 id="version-lock">Version lock</h2> | |
100 | |
101 <p>Instead, when you depend on a package, your app will only use a single copy o
f | |
102 that package. When you have a shared dependency, everything that depends on it | |
103 has to agree on which version to use. If they don’t, you get an error.</p> | |
104 | |
105 <p>That doesn’t actually solve your problem though. When you <em>do</em> g
et that error, | |
106 you need to be able to resolve it. So let’s say you’ve gotten yourse
lf into | |
107 that situation in the above example. You want to use <code>widgets</code> and <c
ode>templates</code>, | |
108 but they are using different versions of <code>collections</code>. What do you d
o?</p> | |
109 | |
110 <p>The answer is to try to upgrade one of those. <code>templates</code> wants | |
111 <code>collections 2.3.7</code>. Is there a later version of <code>widgets</code>
that you can upgrade | |
112 to that works with that version?</p> | |
113 | |
114 <p>In many cases, the answer will be “no”. Look at it from the persp
ective of the | |
115 people developing <code>widgets</code>. They want to put out a new version with
new changes | |
116 to <em>their</em> code, and they want as many people to be able to upgrade to it
it as | |
117 possible. If they stick to their <em>current</em> version of <code>collections</
code> then anyone | |
118 who is using the current version <code>widgets</code> will be able to drop in th
is new one | |
119 too.</p> | |
120 | |
121 <p>If they were to upgrade <em>their</em> dependency on <code>collections</code>
then everyone who | |
122 upgrades <code>widgets</code> would have to as well, <em>whether they want to or
not.</em> That’s | |
123 painful, so you end up with a disincentive to upgrade dependencies. That’s | |
124 called <strong>version lock</strong>: everyone wants to move their dependencies
forward, but | |
125 no one can take the first step because it forces everyone else to as well.</p> | |
126 | |
127 <h2 id="version-constraints">Version constraints</h2> | |
128 | |
129 <p>To solve version lock, we loosen the constraints that packages place on their | |
130 dependencies. If <code>widgets</code> and <code>templates</code> can both indica
te a <em>range</em> of | |
131 versions for <code>collections</code> that they will work with, then that gives
us enough | |
132 wiggle room to move our dependencies forward to newer versions. As long as there | |
133 is overlap in their ranges, we can still find a single version that makes them | |
134 both happy.</p> | |
135 | |
136 <p>This is the model that <a href="http://gembundler.com/">bundler</a> follows,
and is pub’s | |
137 model too. When you add a dependency in your pubspec, you can specify a <em>rang
e</em> | |
138 of versions that you can accept. If the pubspec for <code>widgets</code> looked
like this:</p> | |
139 | |
140 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">depe
ndencies</span><span class="p-Indicator">:</span> | |
141 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp
an> <span class="s">'>=2.3.5</span><span class="nv"> </span><span class="
s"><2.4.0'</span> | |
142 </code></pre></div> | |
143 | |
144 <p>Then we could pick version <code>2.3.7</code> for <code>collections</code> an
d then both <code>widgets</code> | |
145 and <code>templates</code> have their constraints satisfied by a single concrete
version.</p> | |
146 | |
147 <h2 id="semantic-versions">Semantic versions</h2> | |
148 | |
149 <p>When you add a dependency to your package, you’ll sometimes want to spe
cify a | |
150 range of versions to allow. How do you know what range to pick? You need to | |
151 forward compatible, so ideally the range encompasses future versions that | |
152 haven’t been released yet. But how do you know your package is going to wo
rk | |
153 with some new version that doesn’t even exist yet?</p> | |
154 | |
155 <p>To solve that, you need to agree on what a version number <em>means</em>. Ima
gine that | |
156 the developers of a package you depend on say, “If we make any backwards | |
157 incompatible change, then we promise to increment the major version number.&rdqu
o; | |
158 If you trust them, then if you know your package works with <code>2.5.7</code> o
f theirs, | |
159 you can rely on it working all the way up to <code>3.0.0</code>. So you can set
your range | |
160 like:</p> | |
161 | |
162 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">depe
ndencies</span><span class="p-Indicator">:</span> | |
163 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp
an> <span class="s">'>=2.3.5</span><span class="nv"> </span><span class="
s"><3.0.0'</span> | |
164 </code></pre></div> | |
165 | |
166 <p>To make this work, then, we need to come up with that set of promises. | |
167 Fortunately, other smart people have done the work of figuring this all out and | |
168 named it <a href="http://semver.org/"><em>semantic versioning</em></a>.</p> | |
169 | |
170 <p>That describes the format of a version number, and the exact API behavioral | |
171 differences when you increment to a later version number. Pub requires versions | |
172 to be formatted that way, and to play well with the pub community, your package | |
173 should follow the semantics it specifies. You should assume that the packages | |
174 you depend on also follow it. (And if you find out they don’t, let their | |
175 authors know!)</p> | |
176 | |
177 <p>We’ve got almost all of the pieces we need to deal with versioning and
API | |
178 evolution now. Let’s see how they play together and what pub does.</p> | |
179 | |
180 <h2 id="constraint-solving">Constraint solving</h2> | |
181 | |
182 <p>When you define your package, you list its | |
183 <a href="glossary.html#immediate-dependency"><strong>immediate dependencies</str
ong></a>—the | |
184 packages it itself uses. For each one, you specify the range of versions it | |
185 allows. Each of those dependent packages may in turn have their own | |
186 dependencies (called | |
187 <a href="glossary.html#transitive-dependency"><strong>transitive dependencies</s
trong></a>. Pub will | |
188 traverse these and build up the entire deep dependency graph for your app.</p> | |
189 | |
190 <p>For each package in the graph, pub looks at everything that depends on it. It | |
191 gathers together all of their version constraints and tries to simultaneously | |
192 solve them. (Basically, it intersects their ranges.) Then it looks at the | |
193 actual versions that have been released for that package and selects the best | |
194 (most recent) one that meets all of those constraints.</p> | |
195 | |
196 <p>For example, let’s say our dependency graph contains <code>collections<
/code>, and three | |
197 packages depend on it. Their version constraints are:</p> | |
198 | |
199 <pre><code>>=1.7.0 | |
200 >=1.4.0 <2.0.0 | |
201 <1.9.0 | |
202 </code></pre> | |
203 | |
204 <p>The developers of <code>collections</code> have released these versions of it
:</p> | |
205 | |
206 <pre><code>1.7.0 | |
207 1.7.1 | |
208 1.8.0 | |
209 1.8.1 | |
210 1.8.2 | |
211 1.9.0 | |
212 </code></pre> | |
213 | |
214 <p>The highest version number that fits in all of those ranges is <code>1.8.2</c
ode>, so pub | |
215 picks that. That means your app <em>and every package your app uses</em> will al
l use | |
216 <code>collections 1.8.2</code>.</p> | |
217 | |
218 <h2 id="constraint-context">Constraint context</h2> | |
219 | |
220 <p>The fact that selecting a package version takes into account <em>every</em> p
ackage | |
221 that depends on it has an important consequence: <em>the specific version that | |
222 will be selected for a package is a global property of the app using that | |
223 package.</em></p> | |
224 | |
225 <p>I’ll walk through an example so you can see what this means. Let’
s say we have | |
226 two apps. Here are their pubspecs:</p> | |
227 | |
228 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name
</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">my_app</s
pan> | |
229 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa
n> | |
230 <span class="l-Scalar-Plain">widgets</span><span class="p-Indicator">:</span> | |
231 </code></pre></div> | |
232 | |
233 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name
</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">other_app
</span> | |
234 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa
n> | |
235 <span class="l-Scalar-Plain">widgets</span><span class="p-Indicator">:</span> | |
236 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp
an> <span class="s">'<1.5.0'</span> | |
237 </code></pre></div> | |
238 | |
239 <p>They both depend on <code>widgets</code>, whose pubspec is:</p> | |
240 | |
241 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name
</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">widgets</
span> | |
242 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa
n> | |
243 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp
an> <span class="s">'>=1.0.0</span><span class="nv"> </span><span class="
s"><2.0.0'</span> | |
244 </code></pre></div> | |
245 | |
246 <p>The <code>other_app</code> package uses depends directly on <code>collections
</code> itself. The | |
247 interesting part is that it happens to have a different version constraint on | |
248 it than <code>widgets</code> does.</p> | |
249 | |
250 <p>What this means is that you can’t just look at the <code>widgets</code>
package in | |
251 isolation to figure out what version of <code>collections</code> it will use. It
depends | |
252 on the context. In <code>my_app</code>, <code>widgets</code> will be using <code
>collections 1.9.9</code>. But | |
253 in <code>other_app</code>, <code>widgets</code> will get saddled with <code>coll
ections 1.4.9</code> because of | |
254 the <em>other</em> constraint that <code>otherapp</code> places on it.</p> | |
255 | |
256 <p>This is why each app gets its own “packages” directory: The concr
ete version | |
257 selected for each package depends on the entire dependency graph of the | |
258 containing app.</p> | |
259 | |
260 <h2 id="lockfiles">Lockfiles</h2> | |
261 | |
262 <p>So once pub has solved your app’s version constraints, then what? The e
nd | |
263 result is a complete list of every package that your app depends on either | |
264 directly or indirectly and the best version of that package that will work with | |
265 your app’s constraints.</p> | |
266 | |
267 <p>Pub takes that and writes it out to a <strong>lockfile</strong> in your app&r
squo;s directory | |
268 called <code>pubspec.lock</code>. When pub builds the “packages” dir
ectory your app, it | |
269 uses the lockfile to know what versions of each package to pull in. (And if | |
270 you’re curious to see what versions it selected, you can read the lockfile
to | |
271 find out.)</p> | |
272 | |
273 <p>The next important thing pub does is it <em>stops touching the lockfile</em>.
Once | |
274 you’ve got a lockfile for your app, pub won’t mess with it until you
tell it to. | |
275 This is important. It means you won’t spontanteously start using new versi
ons | |
276 of random packages in your app without intending to. Once your app is locked, | |
277 it stays locked until you manually tell it to update the lockfile.</p> | |
278 | |
279 <p>If your package is for an app, you take your lockfile <em>check that bad boy | |
280 into your source control system!</em> That way, everyone on your team will be us
ing | |
281 the exact same versions of every dependency when they hack on your app. You&rsqu
o;ll | |
282 also use this when you deploy your app so you can ensure that your production | |
283 servers are using the exact same packages that you’re developing with.</p> | |
284 | |
285 <h2 id="when-things-go-wrong">When things go wrong</h2> | |
286 | |
287 <p>Of course, all of this presumes that your dependency graph is perfect and | |
288 flawless. Oh, to be so fortunate. Even with version ranges and pub’s const
raint | |
289 solving and semantic versioning, you can never be entirely spared from the | |
290 dangers of version hell.</p> | |
291 | |
292 <p>There are a couple of problems you can run into:</p> | |
293 | |
294 <h3 id="you-can-have-disjoint-constraints">You can have disjoint constraints</h3
> | |
295 | |
296 <p>Lets say your app uses <code>widgets</code> and | |
297 <code>templates</code> and both use <code>collections</code>. But <code>widgets<
/code> asks for a version | |
298 of it between <code>1.0.0</code> and <code>2.0.0</code> and <code>templates</cod
e> wants something | |
299 between <code>3.0.0</code> and <code>4.0.0</code>. Those ranges don’t even
overlap. There’s no | |
300 possible version that would work.</p> | |
301 | |
302 <h3 id="you-can-have-ranges-that-dont-contain-a-released-version">You can have r
anges that don’t contain a released version</h3> | |
303 | |
304 <p>Let’s say after | |
305 putting all of the constraints on a shared dependency together, you’re | |
306 left with the narrow range of <code>>=1.2.4 <1.2.6</code>. It’s not
an empty range. | |
307 If there was a version <code>1.2.4</code> of the dependency, you’d be gold
en. But maybe | |
308 they never released that and instead when straight from <code>1.2.3</code> to <c
ode>1.3.0</code>. | |
309 You’ve got a range but nothing exists inside it.</p> | |
310 | |
311 <h3 id="you-can-have-an-unstable-graph">You can have an unstable graph</h3> | |
312 | |
313 <p>This is, by far, the hairiest part of | |
314 pub’s version solving process. I’ve described the process as “
build up the | |
315 dependency graph and then solve all of the constraints and pick versions”. | |
316 But it doesn’t actually work that way. How could you build up the <em>whol
e</em> | |
317 dependency graph before you’ve picked <em>any</em> versions? <em>The pubsp
ecs | |
318 themselves are version-specific</em>. Different versions of the same package | |
319 may have different sets of dependencies.</p> | |
320 | |
321 <p>As you’re selecting versions of packages, they are changing the shape o
f | |
322 the dependency graph itself. As the graph changes, that may change | |
323 constraints, which can cause you to select different versions, and then you | |
324 go right back around in a circle.</p> | |
325 | |
326 <p>Sometimes this process never settles down into a stable solution. Gaze into | |
327 the abyss:</p> | |
328 | |
329 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name
</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">my_app</s
pan> | |
330 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s
pan class="l-Scalar-Plain">0.0.0</span> | |
331 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa
n> | |
332 <span class="l-Scalar-Plain">yin</span><span class="p-Indicator">:</span> <spa
n class="s">'>=1.0.0'</span> | |
333 </code></pre></div> | |
334 | |
335 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name
</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">yin</span
> | |
336 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s
pan class="l-Scalar-Plain">1.0.0</span> | |
337 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa
n> | |
338 </code></pre></div> | |
339 | |
340 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name
</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">yin</span
> | |
341 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s
pan class="l-Scalar-Plain">2.0.0</span> | |
342 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa
n> | |
343 <span class="l-Scalar-Plain">yang</span><span class="p-Indicator">:</span> <sp
an class="s">'1.0.0'</span> | |
344 </code></pre></div> | |
345 | |
346 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name
</span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">yang</spa
n> | |
347 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s
pan class="l-Scalar-Plain">1.0.0</span> | |
348 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa
n> | |
349 <span class="l-Scalar-Plain">yin</span><span class="p-Indicator">:</span> <spa
n class="s">'1.0.0'</span> | |
350 </code></pre></div> | |
351 | |
352 <p>In all of these cases, there is no set of concrete versions that will work fo
r | |
353 your app, and when this happens pub will report an error and tell you what&rsquo
;s | |
354 going on. It definitely will not try to leave you in some weird state where you | |
355 think things can work but won’t.</p> | |
356 | |
357 <h2 id="summary">Summary</h2> | |
358 | |
359 <p>Wow, that’s a lot to get through. Here’s the important bits:</p> | |
360 | |
361 <ul> | |
362 <li>Code reuse is great, but in order to let developers move quickly, packages | |
363 need to be able to evolve independently.</li> | |
364 <li>Versioning is how you enable that. But depending on single concrete versio
ns | |
365 is too precise and with shared dependencies leads to version lock.</li> | |
366 <li>To cope with that, you depend on <em>ranges</em> of versions. Pub will the
n walk | |
367 your dependency graph and pick the best versions for you. If it can’t, it | |
368 tells you.</li> | |
369 <li>Once your app has a solid set of versions for its dependencies, that gets | |
370 pinned down in a <em>lockfile</em>. That ensures that every machine your app is | |
371 on is using the same versions of all of its dependencies.</li> | |
372 </ul> | |
OLD | NEW |