summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/tests
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/tests')
-rw-r--r--bitbake/lib/bb/tests/codeparser.py106
-rw-r--r--bitbake/lib/bb/tests/color.py4
-rw-r--r--bitbake/lib/bb/tests/compression.py100
-rw-r--r--bitbake/lib/bb/tests/cooker.py2
-rw-r--r--bitbake/lib/bb/tests/data.py181
-rw-r--r--bitbake/lib/bb/tests/event.py62
-rw-r--r--bitbake/lib/bb/tests/fetch-testdata/debian/pool/main/m/minicom/index.html59
-rw-r--r--bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.10/index.html20
-rw-r--r--bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.9/index.html40
-rw-r--r--bitbake/lib/bb/tests/fetch-testdata/software/libxml2/index.html19
-rw-r--r--bitbake/lib/bb/tests/fetch-testdata/software/miniupnp/download.php3528
-rw-r--r--bitbake/lib/bb/tests/fetch.py2003
-rw-r--r--bitbake/lib/bb/tests/filter.py88
-rw-r--r--bitbake/lib/bb/tests/parse.py406
-rw-r--r--bitbake/lib/bb/tests/persist_data.py129
-rw-r--r--bitbake/lib/bb/tests/runqueue-tests/classes/base.bbclass4
-rw-r--r--bitbake/lib/bb/tests/runqueue-tests/conf/bitbake.conf2
-rw-r--r--bitbake/lib/bb/tests/runqueue-tests/recipes/g1.bb2
-rw-r--r--bitbake/lib/bb/tests/runqueue-tests/recipes/h1.bb0
-rw-r--r--bitbake/lib/bb/tests/runqueue.py65
-rw-r--r--bitbake/lib/bb/tests/setup.py358
-rw-r--r--bitbake/lib/bb/tests/siggen.py77
-rw-r--r--bitbake/lib/bb/tests/support/httpserver.py4
-rw-r--r--bitbake/lib/bb/tests/utils.py39
24 files changed, 6526 insertions, 772 deletions
diff --git a/bitbake/lib/bb/tests/codeparser.py b/bitbake/lib/bb/tests/codeparser.py
index 826a2d2f6d..c0d1362a0c 100644
--- a/bitbake/lib/bb/tests/codeparser.py
+++ b/bitbake/lib/bb/tests/codeparser.py
@@ -44,6 +44,7 @@ class VariableReferenceTest(ReferenceTest):
44 def parseExpression(self, exp): 44 def parseExpression(self, exp):
45 parsedvar = self.d.expandWithRefs(exp, None) 45 parsedvar = self.d.expandWithRefs(exp, None)
46 self.references = parsedvar.references 46 self.references = parsedvar.references
47 self.execs = parsedvar.execs
47 48
48 def test_simple_reference(self): 49 def test_simple_reference(self):
49 self.setEmptyVars(["FOO"]) 50 self.setEmptyVars(["FOO"])
@@ -61,6 +62,11 @@ class VariableReferenceTest(ReferenceTest):
61 self.parseExpression("${@d.getVar('BAR') + 'foo'}") 62 self.parseExpression("${@d.getVar('BAR') + 'foo'}")
62 self.assertReferences(set(["BAR"])) 63 self.assertReferences(set(["BAR"]))
63 64
65 def test_python_exec_reference(self):
66 self.parseExpression("${@eval('3 * 5')}")
67 self.assertReferences(set())
68 self.assertExecs(set(["eval"]))
69
64class ShellReferenceTest(ReferenceTest): 70class ShellReferenceTest(ReferenceTest):
65 71
66 def parseExpression(self, exp): 72 def parseExpression(self, exp):
@@ -100,6 +106,46 @@ ${D}${libdir}/pkgconfig/*.pc
100 self.parseExpression("foo=$(echo bar)") 106 self.parseExpression("foo=$(echo bar)")
101 self.assertExecs(set(["echo"])) 107 self.assertExecs(set(["echo"]))
102 108
109 def test_assign_subshell_expansion_quotes(self):
110 self.parseExpression('foo="$(echo bar)"')
111 self.assertExecs(set(["echo"]))
112
113 def test_assign_subshell_expansion_nested(self):
114 self.parseExpression('foo="$(func1 "$(func2 bar$(func3))")"')
115 self.assertExecs(set(["func1", "func2", "func3"]))
116
117 def test_assign_subshell_expansion_multiple(self):
118 self.parseExpression('foo="$(func1 "$(func2)") $(func3)"')
119 self.assertExecs(set(["func1", "func2", "func3"]))
120
121 def test_assign_subshell_expansion_escaped_quotes(self):
122 self.parseExpression('foo="\\"fo\\"o$(func1)"')
123 self.assertExecs(set(["func1"]))
124
125 def test_assign_subshell_expansion_empty(self):
126 self.parseExpression('foo="bar$()foo"')
127 self.assertExecs(set())
128
129 def test_assign_subshell_backticks(self):
130 self.parseExpression("foo=`echo bar`")
131 self.assertExecs(set(["echo"]))
132
133 def test_assign_subshell_backticks_quotes(self):
134 self.parseExpression('foo="`echo bar`"')
135 self.assertExecs(set(["echo"]))
136
137 def test_assign_subshell_backticks_multiple(self):
138 self.parseExpression('foo="`func1 bar` `func2`"')
139 self.assertExecs(set(["func1", "func2"]))
140
141 def test_assign_subshell_backticks_escaped_quotes(self):
142 self.parseExpression('foo="\\"fo\\"o`func1`"')
143 self.assertExecs(set(["func1"]))
144
145 def test_assign_subshell_backticks_empty(self):
146 self.parseExpression('foo="bar``foo"')
147 self.assertExecs(set())
148
103 def test_shell_unexpanded(self): 149 def test_shell_unexpanded(self):
104 self.setEmptyVars(["QT_BASE_NAME"]) 150 self.setEmptyVars(["QT_BASE_NAME"])
105 self.parseExpression('echo "${QT_BASE_NAME}"') 151 self.parseExpression('echo "${QT_BASE_NAME}"')
@@ -111,9 +157,9 @@ ${D}${libdir}/pkgconfig/*.pc
111 self.assertExecs(set(["sed"])) 157 self.assertExecs(set(["sed"]))
112 158
113 def test_parameter_expansion_modifiers(self): 159 def test_parameter_expansion_modifiers(self):
114 # - and + are also valid modifiers for parameter expansion, but are 160 # -,+ and : are also valid modifiers for parameter expansion, but are
115 # valid characters in bitbake variable names, so are not included here 161 # valid characters in bitbake variable names, so are not included here
116 for i in ('=', ':-', ':=', '?', ':?', ':+', '#', '%', '##', '%%'): 162 for i in ('=', '?', '#', '%', '##', '%%'):
117 name = "foo%sbar" % i 163 name = "foo%sbar" % i
118 self.parseExpression("${%s}" % name) 164 self.parseExpression("${%s}" % name)
119 self.assertNotIn(name, self.references) 165 self.assertNotIn(name, self.references)
@@ -318,7 +364,7 @@ d.getVar(a(), False)
318 "filename": "example.bb", 364 "filename": "example.bb",
319 }) 365 })
320 366
321 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d) 367 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), set(), set(), self.d, self.d)
322 368
323 self.assertEqual(deps, set(["somevar", "bar", "something", "inexpand", "test", "test2", "a"])) 369 self.assertEqual(deps, set(["somevar", "bar", "something", "inexpand", "test", "test2", "a"]))
324 370
@@ -365,7 +411,7 @@ esac
365 self.d.setVarFlags("FOO", {"func": True}) 411 self.d.setVarFlags("FOO", {"func": True})
366 self.setEmptyVars(execs) 412 self.setEmptyVars(execs)
367 413
368 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d) 414 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), set(), set(), self.d, self.d)
369 415
370 self.assertEqual(deps, set(["somevar", "inverted"] + execs)) 416 self.assertEqual(deps, set(["somevar", "inverted"] + execs))
371 417
@@ -375,7 +421,7 @@ esac
375 self.d.setVar("FOO", "foo=oe_libinstall; eval $foo") 421 self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
376 self.d.setVarFlag("FOO", "vardeps", "oe_libinstall") 422 self.d.setVarFlag("FOO", "vardeps", "oe_libinstall")
377 423
378 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d) 424 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), set(), set(), self.d, self.d)
379 425
380 self.assertEqual(deps, set(["oe_libinstall"])) 426 self.assertEqual(deps, set(["oe_libinstall"]))
381 427
@@ -384,7 +430,7 @@ esac
384 self.d.setVar("FOO", "foo=oe_libinstall; eval $foo") 430 self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
385 self.d.setVarFlag("FOO", "vardeps", "${@'oe_libinstall'}") 431 self.d.setVarFlag("FOO", "vardeps", "${@'oe_libinstall'}")
386 432
387 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d) 433 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), set(), set(), self.d, self.d)
388 434
389 self.assertEqual(deps, set(["oe_libinstall"])) 435 self.assertEqual(deps, set(["oe_libinstall"]))
390 436
@@ -399,7 +445,7 @@ esac
399 # Check dependencies 445 # Check dependencies
400 self.d.setVar('ANOTHERVAR', expr) 446 self.d.setVar('ANOTHERVAR', expr)
401 self.d.setVar('TESTVAR', 'anothervalue testval testval2') 447 self.d.setVar('TESTVAR', 'anothervalue testval testval2')
402 deps, values = bb.data.build_dependencies("ANOTHERVAR", set(self.d.keys()), set(), set(), self.d) 448 deps, values = bb.data.build_dependencies("ANOTHERVAR", set(self.d.keys()), set(), set(), set(), set(), self.d, self.d)
403 self.assertEqual(sorted(values.splitlines()), 449 self.assertEqual(sorted(values.splitlines()),
404 sorted([expr, 450 sorted([expr,
405 'TESTVAR{anothervalue} = Set', 451 'TESTVAR{anothervalue} = Set',
@@ -412,11 +458,55 @@ esac
412 # Check final value 458 # Check final value
413 self.assertEqual(self.d.getVar('ANOTHERVAR').split(), ['anothervalue', 'yetanothervalue', 'lastone']) 459 self.assertEqual(self.d.getVar('ANOTHERVAR').split(), ['anothervalue', 'yetanothervalue', 'lastone'])
414 460
461 def test_contains_vardeps_excluded(self):
462 # Check the ignored_vars option to build_dependencies is handled by contains functionality
463 varval = '${TESTVAR2} ${@bb.utils.filter("TESTVAR", "somevalue anothervalue", d)}'
464 self.d.setVar('ANOTHERVAR', varval)
465 self.d.setVar('TESTVAR', 'anothervalue testval testval2')
466 self.d.setVar('TESTVAR2', 'testval3')
467 deps, values = bb.data.build_dependencies("ANOTHERVAR", set(self.d.keys()), set(), set(), set(), set(["TESTVAR"]), self.d, self.d)
468 self.assertEqual(sorted(values.splitlines()), sorted([varval]))
469 self.assertEqual(deps, set(["TESTVAR2"]))
470 self.assertEqual(self.d.getVar('ANOTHERVAR').split(), ['testval3', 'anothervalue'])
471
472 # Check the vardepsexclude flag is handled by contains functionality
473 self.d.setVarFlag('ANOTHERVAR', 'vardepsexclude', 'TESTVAR')
474 deps, values = bb.data.build_dependencies("ANOTHERVAR", set(self.d.keys()), set(), set(), set(), set(), self.d, self.d)
475 self.assertEqual(sorted(values.splitlines()), sorted([varval]))
476 self.assertEqual(deps, set(["TESTVAR2"]))
477 self.assertEqual(self.d.getVar('ANOTHERVAR').split(), ['testval3', 'anothervalue'])
478
479 def test_contains_vardeps_override_operators(self):
480 # Check override operators handle dependencies correctly with the contains functionality
481 expr_plain = 'testval'
482 expr_prepend = '${@bb.utils.filter("TESTVAR1", "testval1", d)} '
483 expr_append = ' ${@bb.utils.filter("TESTVAR2", "testval2", d)}'
484 expr_remove = '${@bb.utils.contains("TESTVAR3", "no-testval", "testval", "", d)}'
485 # Check dependencies
486 self.d.setVar('ANOTHERVAR', expr_plain)
487 self.d.prependVar('ANOTHERVAR', expr_prepend)
488 self.d.appendVar('ANOTHERVAR', expr_append)
489 self.d.setVar('ANOTHERVAR:remove', expr_remove)
490 self.d.setVar('TESTVAR1', 'blah')
491 self.d.setVar('TESTVAR2', 'testval2')
492 self.d.setVar('TESTVAR3', 'no-testval')
493 deps, values = bb.data.build_dependencies("ANOTHERVAR", set(self.d.keys()), set(), set(), set(), set(), self.d, self.d)
494 self.assertEqual(sorted(values.splitlines()),
495 sorted([
496 expr_prepend + expr_plain + expr_append,
497 '_remove of ' + expr_remove,
498 'TESTVAR1{testval1} = Unset',
499 'TESTVAR2{testval2} = Set',
500 'TESTVAR3{no-testval} = Set',
501 ]))
502 # Check final value
503 self.assertEqual(self.d.getVar('ANOTHERVAR').split(), ['testval2'])
504
415 #Currently no wildcard support 505 #Currently no wildcard support
416 #def test_vardeps_wildcards(self): 506 #def test_vardeps_wildcards(self):
417 # self.d.setVar("oe_libinstall", "echo test") 507 # self.d.setVar("oe_libinstall", "echo test")
418 # self.d.setVar("FOO", "foo=oe_libinstall; eval $foo") 508 # self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
419 # self.d.setVarFlag("FOO", "vardeps", "oe_*") 509 # self.d.setVarFlag("FOO", "vardeps", "oe_*")
420 # self.assertEquals(deps, set(["oe_libinstall"])) 510 # self.assertEqual(deps, set(["oe_libinstall"]))
421 511
422 512
diff --git a/bitbake/lib/bb/tests/color.py b/bitbake/lib/bb/tests/color.py
index bf03750c69..bb70cb393d 100644
--- a/bitbake/lib/bb/tests/color.py
+++ b/bitbake/lib/bb/tests/color.py
@@ -20,7 +20,7 @@ class ProgressWatcher:
20 def __init__(self): 20 def __init__(self):
21 self._reports = [] 21 self._reports = []
22 22
23 def handle_event(self, event): 23 def handle_event(self, event, d):
24 self._reports.append((event.progress, event.rate)) 24 self._reports.append((event.progress, event.rate))
25 25
26 def reports(self): 26 def reports(self):
@@ -31,7 +31,7 @@ class ColorCodeTests(unittest.TestCase):
31 def setUp(self): 31 def setUp(self):
32 self.d = bb.data.init() 32 self.d = bb.data.init()
33 self._progress_watcher = ProgressWatcher() 33 self._progress_watcher = ProgressWatcher()
34 bb.event.register("bb.build.TaskProgress", self._progress_watcher.handle_event) 34 bb.event.register("bb.build.TaskProgress", self._progress_watcher.handle_event, data=self.d)
35 35
36 def tearDown(self): 36 def tearDown(self):
37 bb.event.remove("bb.build.TaskProgress", None) 37 bb.event.remove("bb.build.TaskProgress", None)
diff --git a/bitbake/lib/bb/tests/compression.py b/bitbake/lib/bb/tests/compression.py
new file mode 100644
index 0000000000..16c297b315
--- /dev/null
+++ b/bitbake/lib/bb/tests/compression.py
@@ -0,0 +1,100 @@
1#
2# Copyright BitBake Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7from pathlib import Path
8import bb.compress.lz4
9import bb.compress.zstd
10import contextlib
11import os
12import shutil
13import tempfile
14import unittest
15import subprocess
16
17
18class CompressionTests(object):
19 def setUp(self):
20 self._t = tempfile.TemporaryDirectory()
21 self.tmpdir = Path(self._t.name)
22 self.addCleanup(self._t.cleanup)
23
24 def _file_helper(self, mode_suffix, data):
25 tmp_file = self.tmpdir / "compressed"
26
27 with self.do_open(tmp_file, mode="w" + mode_suffix) as f:
28 f.write(data)
29
30 with self.do_open(tmp_file, mode="r" + mode_suffix) as f:
31 read_data = f.read()
32
33 self.assertEqual(read_data, data)
34
35 def test_text_file(self):
36 self._file_helper("t", "Hello")
37
38 def test_binary_file(self):
39 self._file_helper("b", "Hello".encode("utf-8"))
40
41 def _pipe_helper(self, mode_suffix, data):
42 rfd, wfd = os.pipe()
43 with open(rfd, "rb") as r, open(wfd, "wb") as w:
44 with self.do_open(r, mode="r" + mode_suffix) as decompress:
45 with self.do_open(w, mode="w" + mode_suffix) as compress:
46 compress.write(data)
47 read_data = decompress.read()
48
49 self.assertEqual(read_data, data)
50
51 def test_text_pipe(self):
52 self._pipe_helper("t", "Hello")
53
54 def test_binary_pipe(self):
55 self._pipe_helper("b", "Hello".encode("utf-8"))
56
57 def test_bad_decompress(self):
58 tmp_file = self.tmpdir / "compressed"
59 with tmp_file.open("wb") as f:
60 f.write(b"\x00")
61
62 with self.assertRaises(OSError):
63 with self.do_open(tmp_file, mode="rb", stderr=subprocess.DEVNULL) as f:
64 data = f.read()
65
66
67class LZ4Tests(CompressionTests, unittest.TestCase):
68 def setUp(self):
69 if shutil.which("lz4") is None:
70 self.skipTest("'lz4' not found")
71 super().setUp()
72
73 @contextlib.contextmanager
74 def do_open(self, *args, **kwargs):
75 with bb.compress.lz4.open(*args, **kwargs) as f:
76 yield f
77
78
79class ZStdTests(CompressionTests, unittest.TestCase):
80 def setUp(self):
81 if shutil.which("zstd") is None:
82 self.skipTest("'zstd' not found")
83 super().setUp()
84
85 @contextlib.contextmanager
86 def do_open(self, *args, **kwargs):
87 with bb.compress.zstd.open(*args, **kwargs) as f:
88 yield f
89
90
91class PZStdTests(CompressionTests, unittest.TestCase):
92 def setUp(self):
93 if shutil.which("pzstd") is None:
94 self.skipTest("'pzstd' not found")
95 super().setUp()
96
97 @contextlib.contextmanager
98 def do_open(self, *args, **kwargs):
99 with bb.compress.zstd.open(*args, num_threads=2, **kwargs) as f:
100 yield f
diff --git a/bitbake/lib/bb/tests/cooker.py b/bitbake/lib/bb/tests/cooker.py
index c82d4b7b81..9e524ae345 100644
--- a/bitbake/lib/bb/tests/cooker.py
+++ b/bitbake/lib/bb/tests/cooker.py
@@ -1,6 +1,8 @@
1# 1#
2# BitBake Tests for cooker.py 2# BitBake Tests for cooker.py
3# 3#
4# Copyright BitBake Contributors
5#
4# SPDX-License-Identifier: GPL-2.0-only 6# SPDX-License-Identifier: GPL-2.0-only
5# 7#
6 8
diff --git a/bitbake/lib/bb/tests/data.py b/bitbake/lib/bb/tests/data.py
index 1d4a64b109..a895f6a58e 100644
--- a/bitbake/lib/bb/tests/data.py
+++ b/bitbake/lib/bb/tests/data.py
@@ -60,6 +60,15 @@ class DataExpansions(unittest.TestCase):
60 val = self.d.expand("${@5*12}") 60 val = self.d.expand("${@5*12}")
61 self.assertEqual(str(val), "60") 61 self.assertEqual(str(val), "60")
62 62
63 def test_python_snippet_w_dict(self):
64 val = self.d.expand("${@{ 'green': 1, 'blue': 2 }['green']}")
65 self.assertEqual(str(val), "1")
66
67 def test_python_unexpanded_multi(self):
68 self.d.setVar("bar", "${unsetvar}")
69 val = self.d.expand("${@2*2},${foo},${@d.getVar('foo') + ' ${bar}'},${foo}")
70 self.assertEqual(str(val), "4,value_of_foo,${@d.getVar('foo') + ' ${unsetvar}'},value_of_foo")
71
63 def test_expand_in_python_snippet(self): 72 def test_expand_in_python_snippet(self):
64 val = self.d.expand("${@'boo ' + '${foo}'}") 73 val = self.d.expand("${@'boo ' + '${foo}'}")
65 self.assertEqual(str(val), "boo value_of_foo") 74 self.assertEqual(str(val), "boo value_of_foo")
@@ -68,6 +77,18 @@ class DataExpansions(unittest.TestCase):
68 val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}") 77 val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
69 self.assertEqual(str(val), "value_of_foo value_of_bar") 78 self.assertEqual(str(val), "value_of_foo value_of_bar")
70 79
80 def test_python_snippet_function_reference(self):
81 self.d.setVar("TESTVAL", "testvalue")
82 self.d.setVar("testfunc", 'd.getVar("TESTVAL")')
83 context = bb.utils.get_context()
84 context["testfunc"] = lambda d: d.getVar("TESTVAL")
85 val = self.d.expand("${@testfunc(d)}")
86 self.assertEqual(str(val), "testvalue")
87
88 def test_python_snippet_builtin_metadata(self):
89 self.d.setVar("eval", "INVALID")
90 self.d.expand("${@eval('3')}")
91
71 def test_python_unexpanded(self): 92 def test_python_unexpanded(self):
72 self.d.setVar("bar", "${unsetvar}") 93 self.d.setVar("bar", "${unsetvar}")
73 val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}") 94 val = self.d.expand("${@d.getVar('foo') + ' ${bar}'}")
@@ -245,35 +266,35 @@ class TestConcatOverride(unittest.TestCase):
245 266
246 def test_prepend(self): 267 def test_prepend(self):
247 self.d.setVar("TEST", "${VAL}") 268 self.d.setVar("TEST", "${VAL}")
248 self.d.setVar("TEST_prepend", "${FOO}:") 269 self.d.setVar("TEST:prepend", "${FOO}:")
249 self.assertEqual(self.d.getVar("TEST"), "foo:val") 270 self.assertEqual(self.d.getVar("TEST"), "foo:val")
250 271
251 def test_append(self): 272 def test_append(self):
252 self.d.setVar("TEST", "${VAL}") 273 self.d.setVar("TEST", "${VAL}")
253 self.d.setVar("TEST_append", ":${BAR}") 274 self.d.setVar("TEST:append", ":${BAR}")
254 self.assertEqual(self.d.getVar("TEST"), "val:bar") 275 self.assertEqual(self.d.getVar("TEST"), "val:bar")
255 276
256 def test_multiple_append(self): 277 def test_multiple_append(self):
257 self.d.setVar("TEST", "${VAL}") 278 self.d.setVar("TEST", "${VAL}")
258 self.d.setVar("TEST_prepend", "${FOO}:") 279 self.d.setVar("TEST:prepend", "${FOO}:")
259 self.d.setVar("TEST_append", ":val2") 280 self.d.setVar("TEST:append", ":val2")
260 self.d.setVar("TEST_append", ":${BAR}") 281 self.d.setVar("TEST:append", ":${BAR}")
261 self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar") 282 self.assertEqual(self.d.getVar("TEST"), "foo:val:val2:bar")
262 283
263 def test_append_unset(self): 284 def test_append_unset(self):
264 self.d.setVar("TEST_prepend", "${FOO}:") 285 self.d.setVar("TEST:prepend", "${FOO}:")
265 self.d.setVar("TEST_append", ":val2") 286 self.d.setVar("TEST:append", ":val2")
266 self.d.setVar("TEST_append", ":${BAR}") 287 self.d.setVar("TEST:append", ":${BAR}")
267 self.assertEqual(self.d.getVar("TEST"), "foo::val2:bar") 288 self.assertEqual(self.d.getVar("TEST"), "foo::val2:bar")
268 289
269 def test_remove(self): 290 def test_remove(self):
270 self.d.setVar("TEST", "${VAL} ${BAR}") 291 self.d.setVar("TEST", "${VAL} ${BAR}")
271 self.d.setVar("TEST_remove", "val") 292 self.d.setVar("TEST:remove", "val")
272 self.assertEqual(self.d.getVar("TEST"), " bar") 293 self.assertEqual(self.d.getVar("TEST"), " bar")
273 294
274 def test_remove_cleared(self): 295 def test_remove_cleared(self):
275 self.d.setVar("TEST", "${VAL} ${BAR}") 296 self.d.setVar("TEST", "${VAL} ${BAR}")
276 self.d.setVar("TEST_remove", "val") 297 self.d.setVar("TEST:remove", "val")
277 self.d.setVar("TEST", "${VAL} ${BAR}") 298 self.d.setVar("TEST", "${VAL} ${BAR}")
278 self.assertEqual(self.d.getVar("TEST"), "val bar") 299 self.assertEqual(self.d.getVar("TEST"), "val bar")
279 300
@@ -281,42 +302,42 @@ class TestConcatOverride(unittest.TestCase):
281 # (including that whitespace is preserved) 302 # (including that whitespace is preserved)
282 def test_remove_inactive_override(self): 303 def test_remove_inactive_override(self):
283 self.d.setVar("TEST", "${VAL} ${BAR} 123") 304 self.d.setVar("TEST", "${VAL} ${BAR} 123")
284 self.d.setVar("TEST_remove_inactiveoverride", "val") 305 self.d.setVar("TEST:remove:inactiveoverride", "val")
285 self.assertEqual(self.d.getVar("TEST"), "val bar 123") 306 self.assertEqual(self.d.getVar("TEST"), "val bar 123")
286 307
287 def test_doubleref_remove(self): 308 def test_doubleref_remove(self):
288 self.d.setVar("TEST", "${VAL} ${BAR}") 309 self.d.setVar("TEST", "${VAL} ${BAR}")
289 self.d.setVar("TEST_remove", "val") 310 self.d.setVar("TEST:remove", "val")
290 self.d.setVar("TEST_TEST", "${TEST} ${TEST}") 311 self.d.setVar("TEST_TEST", "${TEST} ${TEST}")
291 self.assertEqual(self.d.getVar("TEST_TEST"), " bar bar") 312 self.assertEqual(self.d.getVar("TEST_TEST"), " bar bar")
292 313
293 def test_empty_remove(self): 314 def test_empty_remove(self):
294 self.d.setVar("TEST", "") 315 self.d.setVar("TEST", "")
295 self.d.setVar("TEST_remove", "val") 316 self.d.setVar("TEST:remove", "val")
296 self.assertEqual(self.d.getVar("TEST"), "") 317 self.assertEqual(self.d.getVar("TEST"), "")
297 318
298 def test_remove_expansion(self): 319 def test_remove_expansion(self):
299 self.d.setVar("BAR", "Z") 320 self.d.setVar("BAR", "Z")
300 self.d.setVar("TEST", "${BAR}/X Y") 321 self.d.setVar("TEST", "${BAR}/X Y")
301 self.d.setVar("TEST_remove", "${BAR}/X") 322 self.d.setVar("TEST:remove", "${BAR}/X")
302 self.assertEqual(self.d.getVar("TEST"), " Y") 323 self.assertEqual(self.d.getVar("TEST"), " Y")
303 324
304 def test_remove_expansion_items(self): 325 def test_remove_expansion_items(self):
305 self.d.setVar("TEST", "A B C D") 326 self.d.setVar("TEST", "A B C D")
306 self.d.setVar("BAR", "B D") 327 self.d.setVar("BAR", "B D")
307 self.d.setVar("TEST_remove", "${BAR}") 328 self.d.setVar("TEST:remove", "${BAR}")
308 self.assertEqual(self.d.getVar("TEST"), "A C ") 329 self.assertEqual(self.d.getVar("TEST"), "A C ")
309 330
310 def test_remove_preserve_whitespace(self): 331 def test_remove_preserve_whitespace(self):
311 # When the removal isn't active, the original value should be preserved 332 # When the removal isn't active, the original value should be preserved
312 self.d.setVar("TEST", " A B") 333 self.d.setVar("TEST", " A B")
313 self.d.setVar("TEST_remove", "C") 334 self.d.setVar("TEST:remove", "C")
314 self.assertEqual(self.d.getVar("TEST"), " A B") 335 self.assertEqual(self.d.getVar("TEST"), " A B")
315 336
316 def test_remove_preserve_whitespace2(self): 337 def test_remove_preserve_whitespace2(self):
317 # When the removal is active preserve the whitespace 338 # When the removal is active preserve the whitespace
318 self.d.setVar("TEST", " A B") 339 self.d.setVar("TEST", " A B")
319 self.d.setVar("TEST_remove", "B") 340 self.d.setVar("TEST:remove", "B")
320 self.assertEqual(self.d.getVar("TEST"), " A ") 341 self.assertEqual(self.d.getVar("TEST"), " A ")
321 342
322class TestOverrides(unittest.TestCase): 343class TestOverrides(unittest.TestCase):
@@ -329,81 +350,86 @@ class TestOverrides(unittest.TestCase):
329 self.assertEqual(self.d.getVar("TEST"), "testvalue") 350 self.assertEqual(self.d.getVar("TEST"), "testvalue")
330 351
331 def test_one_override(self): 352 def test_one_override(self):
332 self.d.setVar("TEST_bar", "testvalue2") 353 self.d.setVar("TEST:bar", "testvalue2")
333 self.assertEqual(self.d.getVar("TEST"), "testvalue2") 354 self.assertEqual(self.d.getVar("TEST"), "testvalue2")
334 355
335 def test_one_override_unset(self): 356 def test_one_override_unset(self):
336 self.d.setVar("TEST2_bar", "testvalue2") 357 self.d.setVar("TEST2:bar", "testvalue2")
337 358
338 self.assertEqual(self.d.getVar("TEST2"), "testvalue2") 359 self.assertEqual(self.d.getVar("TEST2"), "testvalue2")
339 self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar']) 360 self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2:bar'])
340 361
341 def test_multiple_override(self): 362 def test_multiple_override(self):
342 self.d.setVar("TEST_bar", "testvalue2") 363 self.d.setVar("TEST:bar", "testvalue2")
343 self.d.setVar("TEST_local", "testvalue3") 364 self.d.setVar("TEST:local", "testvalue3")
344 self.d.setVar("TEST_foo", "testvalue4") 365 self.d.setVar("TEST:foo", "testvalue4")
345 self.assertEqual(self.d.getVar("TEST"), "testvalue3") 366 self.assertEqual(self.d.getVar("TEST"), "testvalue3")
346 self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local']) 367 self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST:foo', 'OVERRIDES', 'TEST:bar', 'TEST:local'])
347 368
348 def test_multiple_combined_overrides(self): 369 def test_multiple_combined_overrides(self):
349 self.d.setVar("TEST_local_foo_bar", "testvalue3") 370 self.d.setVar("TEST:local:foo:bar", "testvalue3")
350 self.assertEqual(self.d.getVar("TEST"), "testvalue3") 371 self.assertEqual(self.d.getVar("TEST"), "testvalue3")
351 372
352 def test_multiple_overrides_unset(self): 373 def test_multiple_overrides_unset(self):
353 self.d.setVar("TEST2_local_foo_bar", "testvalue3") 374 self.d.setVar("TEST2:local:foo:bar", "testvalue3")
354 self.assertEqual(self.d.getVar("TEST2"), "testvalue3") 375 self.assertEqual(self.d.getVar("TEST2"), "testvalue3")
355 376
356 def test_keyexpansion_override(self): 377 def test_keyexpansion_override(self):
357 self.d.setVar("LOCAL", "local") 378 self.d.setVar("LOCAL", "local")
358 self.d.setVar("TEST_bar", "testvalue2") 379 self.d.setVar("TEST:bar", "testvalue2")
359 self.d.setVar("TEST_${LOCAL}", "testvalue3") 380 self.d.setVar("TEST:${LOCAL}", "testvalue3")
360 self.d.setVar("TEST_foo", "testvalue4") 381 self.d.setVar("TEST:foo", "testvalue4")
361 bb.data.expandKeys(self.d) 382 bb.data.expandKeys(self.d)
362 self.assertEqual(self.d.getVar("TEST"), "testvalue3") 383 self.assertEqual(self.d.getVar("TEST"), "testvalue3")
363 384
364 def test_rename_override(self): 385 def test_rename_override(self):
365 self.d.setVar("ALTERNATIVE_ncurses-tools_class-target", "a") 386 self.d.setVar("ALTERNATIVE:ncurses-tools:class-target", "a")
366 self.d.setVar("OVERRIDES", "class-target") 387 self.d.setVar("OVERRIDES", "class-target")
367 self.d.renameVar("ALTERNATIVE_ncurses-tools", "ALTERNATIVE_lib32-ncurses-tools") 388 self.d.renameVar("ALTERNATIVE:ncurses-tools", "ALTERNATIVE:lib32-ncurses-tools")
368 self.assertEqual(self.d.getVar("ALTERNATIVE_lib32-ncurses-tools"), "a") 389 self.assertEqual(self.d.getVar("ALTERNATIVE:lib32-ncurses-tools"), "a")
369 390
370 def test_underscore_override(self): 391 def test_underscore_override(self):
371 self.d.setVar("TEST_bar", "testvalue2") 392 self.d.setVar("TEST:bar", "testvalue2")
372 self.d.setVar("TEST_some_val", "testvalue3") 393 self.d.setVar("TEST:some_val", "testvalue3")
373 self.d.setVar("TEST_foo", "testvalue4") 394 self.d.setVar("TEST:foo", "testvalue4")
374 self.d.setVar("OVERRIDES", "foo:bar:some_val") 395 self.d.setVar("OVERRIDES", "foo:bar:some_val")
375 self.assertEqual(self.d.getVar("TEST"), "testvalue3") 396 self.assertEqual(self.d.getVar("TEST"), "testvalue3")
376 397
398 # Test an override with _<numeric> in it based on a real world OE issue
399 def test_underscore_override_2(self):
400 self.d.setVar("TARGET_ARCH", "x86_64")
401 self.d.setVar("PN", "test-${TARGET_ARCH}")
402 self.d.setVar("VERSION", "1")
403 self.d.setVar("VERSION:pn-test-${TARGET_ARCH}", "2")
404 self.d.setVar("OVERRIDES", "pn-${PN}")
405 bb.data.expandKeys(self.d)
406 self.assertEqual(self.d.getVar("VERSION"), "2")
407
377 def test_remove_with_override(self): 408 def test_remove_with_override(self):
378 self.d.setVar("TEST_bar", "testvalue2") 409 self.d.setVar("TEST:bar", "testvalue2")
379 self.d.setVar("TEST_some_val", "testvalue3 testvalue5") 410 self.d.setVar("TEST:some_val", "testvalue3 testvalue5")
380 self.d.setVar("TEST_some_val_remove", "testvalue3") 411 self.d.setVar("TEST:some_val:remove", "testvalue3")
381 self.d.setVar("TEST_foo", "testvalue4") 412 self.d.setVar("TEST:foo", "testvalue4")
382 self.d.setVar("OVERRIDES", "foo:bar:some_val") 413 self.d.setVar("OVERRIDES", "foo:bar:some_val")
383 self.assertEqual(self.d.getVar("TEST"), " testvalue5") 414 self.assertEqual(self.d.getVar("TEST"), " testvalue5")
384 415
385 def test_append_and_override_1(self): 416 def test_append_and_override_1(self):
386 self.d.setVar("TEST_append", "testvalue2") 417 self.d.setVar("TEST:append", "testvalue2")
387 self.d.setVar("TEST_bar", "testvalue3") 418 self.d.setVar("TEST:bar", "testvalue3")
388 self.assertEqual(self.d.getVar("TEST"), "testvalue3testvalue2") 419 self.assertEqual(self.d.getVar("TEST"), "testvalue3testvalue2")
389 420
390 def test_append_and_override_2(self): 421 def test_append_and_override_2(self):
391 self.d.setVar("TEST_append_bar", "testvalue2") 422 self.d.setVar("TEST:append:bar", "testvalue2")
392 self.assertEqual(self.d.getVar("TEST"), "testvaluetestvalue2") 423 self.assertEqual(self.d.getVar("TEST"), "testvaluetestvalue2")
393 424
394 def test_append_and_override_3(self): 425 def test_append_and_override_3(self):
395 self.d.setVar("TEST_bar_append", "testvalue2") 426 self.d.setVar("TEST:bar:append", "testvalue2")
396 self.assertEqual(self.d.getVar("TEST"), "testvalue2") 427 self.assertEqual(self.d.getVar("TEST"), "testvalue2")
397 428
398 # Test an override with _<numeric> in it based on a real world OE issue 429 def test_append_and_unused_override(self):
399 def test_underscore_override(self): 430 # Had a bug where an unused override append could return "" instead of None
400 self.d.setVar("TARGET_ARCH", "x86_64") 431 self.d.setVar("BAR:append:unusedoverride", "testvalue2")
401 self.d.setVar("PN", "test-${TARGET_ARCH}") 432 self.assertEqual(self.d.getVar("BAR"), None)
402 self.d.setVar("VERSION", "1")
403 self.d.setVar("VERSION_pn-test-${TARGET_ARCH}", "2")
404 self.d.setVar("OVERRIDES", "pn-${PN}")
405 bb.data.expandKeys(self.d)
406 self.assertEqual(self.d.getVar("VERSION"), "2")
407 433
408class TestKeyExpansion(unittest.TestCase): 434class TestKeyExpansion(unittest.TestCase):
409 def setUp(self): 435 def setUp(self):
@@ -424,17 +450,64 @@ class TestFlags(unittest.TestCase):
424 self.d = bb.data.init() 450 self.d = bb.data.init()
425 self.d.setVar("foo", "value of foo") 451 self.d.setVar("foo", "value of foo")
426 self.d.setVarFlag("foo", "flag1", "value of flag1") 452 self.d.setVarFlag("foo", "flag1", "value of flag1")
453 self.d.setVarFlag("foo", "_defaultval_flag_flag1", "default of flag1")
427 self.d.setVarFlag("foo", "flag2", "value of flag2") 454 self.d.setVarFlag("foo", "flag2", "value of flag2")
455 self.d.setVarFlag("foo", "_defaultval_flag_flag2", "default of flag2")
456 self.d.setVarFlag("foo", "flag3", "value of flag3")
457 self.d.setVarFlag("foo", "_defaultval_flag_flagnovalue", "default of flagnovalue")
428 458
429 def test_setflag(self): 459 def test_setflag(self):
430 self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1") 460 self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1")
431 self.assertEqual(self.d.getVarFlag("foo", "flag2", False), "value of flag2") 461 self.assertEqual(self.d.getVarFlag("foo", "flag2", False), "value of flag2")
462 self.assertDictEqual(
463 self.d.getVarFlags("foo"),
464 {
465 "flag1": "value of flag1",
466 "flag2": "value of flag2",
467 "flag3": "value of flag3",
468 "flagnovalue": "default of flagnovalue",
469 }
470 )
471 self.assertDictEqual(
472 self.d.getVarFlags("foo", internalflags=True),
473 {
474 "_content": "value of foo",
475 "flag1": "value of flag1",
476 "flag2": "value of flag2",
477 "flag3": "value of flag3",
478 "_defaultval_flag_flag1": "default of flag1",
479 "_defaultval_flag_flag2": "default of flag2",
480 "_defaultval_flag_flagnovalue": "default of flagnovalue",
481 }
482 )
432 483
433 def test_delflag(self): 484 def test_delflag(self):
434 self.d.delVarFlag("foo", "flag2") 485 self.d.delVarFlag("foo", "flag2")
486 self.d.delVarFlag("foo", "flag3")
435 self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1") 487 self.assertEqual(self.d.getVarFlag("foo", "flag1", False), "value of flag1")
436 self.assertEqual(self.d.getVarFlag("foo", "flag2", False), None) 488 self.assertEqual(self.d.getVarFlag("foo", "flag2", False), None)
437 489 self.assertDictEqual(
490 self.d.getVarFlags("foo"),
491 {
492 "flag1": "value of flag1",
493 "flagnovalue": "default of flagnovalue",
494 }
495 )
496 self.assertDictEqual(
497 self.d.getVarFlags("foo", internalflags=True),
498 {
499 "_content": "value of foo",
500 "flag1": "value of flag1",
501 "_defaultval_flag_flag1": "default of flag1",
502 "_defaultval_flag_flagnovalue": "default of flagnovalue",
503 }
504 )
505
506 def test_delvar(self):
507 self.d.delVar("foo")
508 self.assertEqual(self.d.getVarFlag("foo", "flag1", False), None)
509 self.assertEqual(self.d.getVarFlag("foo", "flag2", False), None)
510 self.assertEqual(self.d.getVarFlags("foo", internalflags=True), None)
438 511
439class Contains(unittest.TestCase): 512class Contains(unittest.TestCase):
440 def setUp(self): 513 def setUp(self):
@@ -498,7 +571,7 @@ class TaskHash(unittest.TestCase):
498 d.setVar("VAR", "val") 571 d.setVar("VAR", "val")
499 # Adding an inactive removal shouldn't change the hash 572 # Adding an inactive removal shouldn't change the hash
500 d.setVar("BAR", "notbar") 573 d.setVar("BAR", "notbar")
501 d.setVar("MYCOMMAND_remove", "${BAR}") 574 d.setVar("MYCOMMAND:remove", "${BAR}")
502 nexthash = gettask_bashhash("mytask", d) 575 nexthash = gettask_bashhash("mytask", d)
503 self.assertEqual(orighash, nexthash) 576 self.assertEqual(orighash, nexthash)
504 577
diff --git a/bitbake/lib/bb/tests/event.py b/bitbake/lib/bb/tests/event.py
index 9ca7e9bc8e..ef61891d30 100644
--- a/bitbake/lib/bb/tests/event.py
+++ b/bitbake/lib/bb/tests/event.py
@@ -13,6 +13,7 @@ import pickle
13import threading 13import threading
14import time 14import time
15import unittest 15import unittest
16import tempfile
16from unittest.mock import Mock 17from unittest.mock import Mock
17from unittest.mock import call 18from unittest.mock import call
18 19
@@ -157,7 +158,7 @@ class EventHandlingTest(unittest.TestCase):
157 self._test_process.event_handler, 158 self._test_process.event_handler,
158 event, 159 event,
159 None) 160 None)
160 self._test_process.event_handler.assert_called_once_with(event) 161 self._test_process.event_handler.assert_called_once_with(event, None)
161 162
162 def test_fire_class_handlers(self): 163 def test_fire_class_handlers(self):
163 """ Test fire_class_handlers method """ 164 """ Test fire_class_handlers method """
@@ -175,10 +176,10 @@ class EventHandlingTest(unittest.TestCase):
175 bb.event.fire_class_handlers(event1, None) 176 bb.event.fire_class_handlers(event1, None)
176 bb.event.fire_class_handlers(event2, None) 177 bb.event.fire_class_handlers(event2, None)
177 bb.event.fire_class_handlers(event2, None) 178 bb.event.fire_class_handlers(event2, None)
178 expected_event_handler1 = [call(event1)] 179 expected_event_handler1 = [call(event1, None)]
179 expected_event_handler2 = [call(event1), 180 expected_event_handler2 = [call(event1, None),
180 call(event2), 181 call(event2, None),
181 call(event2)] 182 call(event2, None)]
182 self.assertEqual(self._test_process.event_handler1.call_args_list, 183 self.assertEqual(self._test_process.event_handler1.call_args_list,
183 expected_event_handler1) 184 expected_event_handler1)
184 self.assertEqual(self._test_process.event_handler2.call_args_list, 185 self.assertEqual(self._test_process.event_handler2.call_args_list,
@@ -205,7 +206,7 @@ class EventHandlingTest(unittest.TestCase):
205 bb.event.fire_class_handlers(event2, None) 206 bb.event.fire_class_handlers(event2, None)
206 bb.event.fire_class_handlers(event2, None) 207 bb.event.fire_class_handlers(event2, None)
207 expected_event_handler1 = [] 208 expected_event_handler1 = []
208 expected_event_handler2 = [call(event1)] 209 expected_event_handler2 = [call(event1, None)]
209 self.assertEqual(self._test_process.event_handler1.call_args_list, 210 self.assertEqual(self._test_process.event_handler1.call_args_list,
210 expected_event_handler1) 211 expected_event_handler1)
211 self.assertEqual(self._test_process.event_handler2.call_args_list, 212 self.assertEqual(self._test_process.event_handler2.call_args_list,
@@ -223,7 +224,7 @@ class EventHandlingTest(unittest.TestCase):
223 self.assertEqual(result, bb.event.Registered) 224 self.assertEqual(result, bb.event.Registered)
224 bb.event.fire_class_handlers(event1, None) 225 bb.event.fire_class_handlers(event1, None)
225 bb.event.fire_class_handlers(event2, None) 226 bb.event.fire_class_handlers(event2, None)
226 expected = [call(event1), call(event2)] 227 expected = [call(event1, None), call(event2, None)]
227 self.assertEqual(self._test_process.event_handler1.call_args_list, 228 self.assertEqual(self._test_process.event_handler1.call_args_list,
228 expected) 229 expected)
229 230
@@ -237,7 +238,7 @@ class EventHandlingTest(unittest.TestCase):
237 self.assertEqual(result, bb.event.Registered) 238 self.assertEqual(result, bb.event.Registered)
238 bb.event.fire_class_handlers(event1, None) 239 bb.event.fire_class_handlers(event1, None)
239 bb.event.fire_class_handlers(event2, None) 240 bb.event.fire_class_handlers(event2, None)
240 expected = [call(event1), call(event2), call(event1)] 241 expected = [call(event1, None), call(event2, None), call(event1, None)]
241 self.assertEqual(self._test_process.event_handler1.call_args_list, 242 self.assertEqual(self._test_process.event_handler1.call_args_list,
242 expected) 243 expected)
243 244
@@ -251,7 +252,7 @@ class EventHandlingTest(unittest.TestCase):
251 self.assertEqual(result, bb.event.Registered) 252 self.assertEqual(result, bb.event.Registered)
252 bb.event.fire_class_handlers(event1, None) 253 bb.event.fire_class_handlers(event1, None)
253 bb.event.fire_class_handlers(event2, None) 254 bb.event.fire_class_handlers(event2, None)
254 expected = [call(event1), call(event2), call(event1), call(event2)] 255 expected = [call(event1,None), call(event2, None), call(event1, None), call(event2, None)]
255 self.assertEqual(self._test_process.event_handler1.call_args_list, 256 self.assertEqual(self._test_process.event_handler1.call_args_list,
256 expected) 257 expected)
257 258
@@ -359,9 +360,10 @@ class EventHandlingTest(unittest.TestCase):
359 360
360 event1 = bb.event.ConfigParsed() 361 event1 = bb.event.ConfigParsed()
361 bb.event.fire(event1, None) 362 bb.event.fire(event1, None)
362 expected = [call(event1)] 363 expected = [call(event1, None)]
363 self.assertEqual(self._test_process.event_handler1.call_args_list, 364 self.assertEqual(self._test_process.event_handler1.call_args_list,
364 expected) 365 expected)
366 expected = [call(event1)]
365 self.assertEqual(self._test_ui1.event.send.call_args_list, 367 self.assertEqual(self._test_ui1.event.send.call_args_list,
366 expected) 368 expected)
367 369
@@ -450,10 +452,9 @@ class EventHandlingTest(unittest.TestCase):
450 and disable threadlocks tests """ 452 and disable threadlocks tests """
451 bb.event.fire(bb.event.OperationStarted(), None) 453 bb.event.fire(bb.event.OperationStarted(), None)
452 454
453 def test_enable_threadlock(self): 455 def test_event_threadlock(self):
454 """ Test enable_threadlock method """ 456 """ Test enable_threadlock method """
455 self._set_threadlock_test_mockups() 457 self._set_threadlock_test_mockups()
456 bb.event.enable_threadlock()
457 self._set_and_run_threadlock_test_workers() 458 self._set_and_run_threadlock_test_workers()
458 # Calls to UI handlers should be in order as all the registered 459 # Calls to UI handlers should be in order as all the registered
459 # handlers for the event coming from the first worker should be 460 # handlers for the event coming from the first worker should be
@@ -461,20 +462,6 @@ class EventHandlingTest(unittest.TestCase):
461 self.assertEqual(self._threadlock_test_calls, 462 self.assertEqual(self._threadlock_test_calls,
462 ["w1_ui1", "w1_ui2", "w2_ui1", "w2_ui2"]) 463 ["w1_ui1", "w1_ui2", "w2_ui1", "w2_ui2"])
463 464
464
465 def test_disable_threadlock(self):
466 """ Test disable_threadlock method """
467 self._set_threadlock_test_mockups()
468 bb.event.disable_threadlock()
469 self._set_and_run_threadlock_test_workers()
470 # Calls to UI handlers should be intertwined together. Thanks to the
471 # delay in the registered handlers for the event coming from the first
472 # worker, the event coming from the second worker starts being
473 # processed before finishing handling the first worker event.
474 self.assertEqual(self._threadlock_test_calls,
475 ["w1_ui1", "w2_ui1", "w1_ui2", "w2_ui2"])
476
477
478class EventClassesTest(unittest.TestCase): 465class EventClassesTest(unittest.TestCase):
479 """ Event classes test class """ 466 """ Event classes test class """
480 467
@@ -482,6 +469,8 @@ class EventClassesTest(unittest.TestCase):
482 469
483 def setUp(self): 470 def setUp(self):
484 bb.event.worker_pid = EventClassesTest._worker_pid 471 bb.event.worker_pid = EventClassesTest._worker_pid
472 self.d = bb.data.init()
473 bb.parse.siggen = bb.siggen.init(self.d)
485 474
486 def test_Event(self): 475 def test_Event(self):
487 """ Test the Event base class """ 476 """ Test the Event base class """
@@ -964,3 +953,24 @@ class EventClassesTest(unittest.TestCase):
964 event = bb.event.FindSigInfoResult(result) 953 event = bb.event.FindSigInfoResult(result)
965 self.assertEqual(event.result, result) 954 self.assertEqual(event.result, result)
966 self.assertEqual(event.pid, EventClassesTest._worker_pid) 955 self.assertEqual(event.pid, EventClassesTest._worker_pid)
956
957 def test_lineno_in_eventhandler(self):
958 # The error lineno is 5, not 4 since the first line is '\n'
959 error_line = """
960# Comment line1
961# Comment line2
962python test_lineno_in_eventhandler() {
963 This is an error line
964}
965addhandler test_lineno_in_eventhandler
966test_lineno_in_eventhandler[eventmask] = "bb.event.ConfigParsed"
967"""
968
969 with self.assertLogs() as logs:
970 f = tempfile.NamedTemporaryFile(suffix = '.bb')
971 f.write(bytes(error_line, "utf-8"))
972 f.flush()
973 d = bb.parse.handle(f.name, self.d)['']
974
975 output = "".join(logs.output)
976 self.assertTrue(" line 5\n" in output)
diff --git a/bitbake/lib/bb/tests/fetch-testdata/debian/pool/main/m/minicom/index.html b/bitbake/lib/bb/tests/fetch-testdata/debian/pool/main/m/minicom/index.html
new file mode 100644
index 0000000000..4a1eb4de13
--- /dev/null
+++ b/bitbake/lib/bb/tests/fetch-testdata/debian/pool/main/m/minicom/index.html
@@ -0,0 +1,59 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
2<html>
3 <head>
4 <title>Index of /debian/pool/main/m/minicom</title>
5 </head>
6 <body>
7<h1>Index of /debian/pool/main/m/minicom</h1>
8 <table>
9 <tr><th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th></tr>
10 <tr><th colspan="4"><hr></th></tr>
11<tr><td valign="top"><img src="/icons/back.gif" alt="[PARENTDIR]"></td><td><a href="/debian/pool/main/m/">Parent Directory</a></td><td>&nbsp;</td><td align="right"> - </td></tr>
12<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1+deb8u1.debian.tar.xz">minicom_2.7-1+deb8u1.debian.tar.xz</a></td><td align="right">2017-04-24 08:22 </td><td align="right"> 14K</td></tr>
13<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1+deb8u1.dsc">minicom_2.7-1+deb8u1.dsc</a></td><td align="right">2017-04-24 08:22 </td><td align="right">1.9K</td></tr>
14<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1+deb8u1_amd64.deb">minicom_2.7-1+deb8u1_amd64.deb</a></td><td align="right">2017-04-25 21:10 </td><td align="right">257K</td></tr>
15<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1+deb8u1_armel.deb">minicom_2.7-1+deb8u1_armel.deb</a></td><td align="right">2017-04-26 00:58 </td><td align="right">246K</td></tr>
16<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1+deb8u1_armhf.deb">minicom_2.7-1+deb8u1_armhf.deb</a></td><td align="right">2017-04-26 00:58 </td><td align="right">245K</td></tr>
17<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1+deb8u1_i386.deb">minicom_2.7-1+deb8u1_i386.deb</a></td><td align="right">2017-04-25 21:41 </td><td align="right">258K</td></tr>
18<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1.debian.tar.xz">minicom_2.7-1.1.debian.tar.xz</a></td><td align="right">2017-04-22 09:34 </td><td align="right"> 14K</td></tr>
19<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1.dsc">minicom_2.7-1.1.dsc</a></td><td align="right">2017-04-22 09:34 </td><td align="right">1.9K</td></tr>
20<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_amd64.deb">minicom_2.7-1.1_amd64.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">261K</td></tr>
21<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_arm64.deb">minicom_2.7-1.1_arm64.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">250K</td></tr>
22<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_armel.deb">minicom_2.7-1.1_armel.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">255K</td></tr>
23<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_armhf.deb">minicom_2.7-1.1_armhf.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">254K</td></tr>
24<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_i386.deb">minicom_2.7-1.1_i386.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">266K</td></tr>
25<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_mips.deb">minicom_2.7-1.1_mips.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">258K</td></tr>
26<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_mips64el.deb">minicom_2.7-1.1_mips64el.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">259K</td></tr>
27<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_mipsel.deb">minicom_2.7-1.1_mipsel.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">259K</td></tr>
28<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_ppc64el.deb">minicom_2.7-1.1_ppc64el.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">253K</td></tr>
29<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7-1.1_s390x.deb">minicom_2.7-1.1_s390x.deb</a></td><td align="right">2017-04-22 15:29 </td><td align="right">261K</td></tr>
30<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_amd64.deb">minicom_2.7.1-1+b1_amd64.deb</a></td><td align="right">2018-05-06 08:14 </td><td align="right">262K</td></tr>
31<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_arm64.deb">minicom_2.7.1-1+b1_arm64.deb</a></td><td align="right">2018-05-06 07:58 </td><td align="right">250K</td></tr>
32<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_armel.deb">minicom_2.7.1-1+b1_armel.deb</a></td><td align="right">2018-05-06 08:45 </td><td align="right">253K</td></tr>
33<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_armhf.deb">minicom_2.7.1-1+b1_armhf.deb</a></td><td align="right">2018-05-06 10:42 </td><td align="right">253K</td></tr>
34<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_i386.deb">minicom_2.7.1-1+b1_i386.deb</a></td><td align="right">2018-05-06 08:55 </td><td align="right">266K</td></tr>
35<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_mips.deb">minicom_2.7.1-1+b1_mips.deb</a></td><td align="right">2018-05-06 08:14 </td><td align="right">258K</td></tr>
36<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_mipsel.deb">minicom_2.7.1-1+b1_mipsel.deb</a></td><td align="right">2018-05-06 12:13 </td><td align="right">259K</td></tr>
37<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_ppc64el.deb">minicom_2.7.1-1+b1_ppc64el.deb</a></td><td align="right">2018-05-06 09:10 </td><td align="right">260K</td></tr>
38<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b1_s390x.deb">minicom_2.7.1-1+b1_s390x.deb</a></td><td align="right">2018-05-06 08:14 </td><td align="right">257K</td></tr>
39<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1+b2_mips64el.deb">minicom_2.7.1-1+b2_mips64el.deb</a></td><td align="right">2018-05-06 09:41 </td><td align="right">260K</td></tr>
40<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1.debian.tar.xz">minicom_2.7.1-1.debian.tar.xz</a></td><td align="right">2017-08-13 15:40 </td><td align="right"> 14K</td></tr>
41<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.7.1-1.dsc">minicom_2.7.1-1.dsc</a></td><td align="right">2017-08-13 15:40 </td><td align="right">1.8K</td></tr>
42<tr><td valign="top"><img src="/icons/compressed.gif" alt="[ ]"></td><td><a href="minicom_2.7.1.orig.tar.gz">minicom_2.7.1.orig.tar.gz</a></td><td align="right">2017-08-13 15:40 </td><td align="right">855K</td></tr>
43<tr><td valign="top"><img src="/icons/compressed.gif" alt="[ ]"></td><td><a href="minicom_2.7.orig.tar.gz">minicom_2.7.orig.tar.gz</a></td><td align="right">2014-01-01 09:36 </td><td align="right">843K</td></tr>
44<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2.debian.tar.xz">minicom_2.8-2.debian.tar.xz</a></td><td align="right">2021-06-15 03:47 </td><td align="right"> 14K</td></tr>
45<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2.dsc">minicom_2.8-2.dsc</a></td><td align="right">2021-06-15 03:47 </td><td align="right">1.8K</td></tr>
46<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_amd64.deb">minicom_2.8-2_amd64.deb</a></td><td align="right">2021-06-15 03:58 </td><td align="right">280K</td></tr>
47<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_arm64.deb">minicom_2.8-2_arm64.deb</a></td><td align="right">2021-06-15 04:13 </td><td align="right">275K</td></tr>
48<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_armel.deb">minicom_2.8-2_armel.deb</a></td><td align="right">2021-06-15 04:13 </td><td align="right">271K</td></tr>
49<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_armhf.deb">minicom_2.8-2_armhf.deb</a></td><td align="right">2021-06-15 04:13 </td><td align="right">272K</td></tr>
50<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_i386.deb">minicom_2.8-2_i386.deb</a></td><td align="right">2021-06-15 04:13 </td><td align="right">285K</td></tr>
51<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_mips64el.deb">minicom_2.8-2_mips64el.deb</a></td><td align="right">2021-06-15 04:13 </td><td align="right">277K</td></tr>
52<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_mipsel.deb">minicom_2.8-2_mipsel.deb</a></td><td align="right">2021-06-15 04:13 </td><td align="right">278K</td></tr>
53<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_ppc64el.deb">minicom_2.8-2_ppc64el.deb</a></td><td align="right">2021-06-15 04:13 </td><td align="right">286K</td></tr>
54<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8-2_s390x.deb">minicom_2.8-2_s390x.deb</a></td><td align="right">2021-06-15 03:58 </td><td align="right">275K</td></tr>
55<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="minicom_2.8.orig.tar.bz2">minicom_2.8.orig.tar.bz2</a></td><td align="right">2021-01-03 12:44 </td><td align="right">598K</td></tr>
56 <tr><th colspan="4"><hr></th></tr>
57</table>
58<address>Apache Server at ftp.debian.org Port 80</address>
59</body></html>
diff --git a/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.10/index.html b/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.10/index.html
new file mode 100644
index 0000000000..4e41af6d6a
--- /dev/null
+++ b/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.10/index.html
@@ -0,0 +1,20 @@
1<!DOCTYPE html><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width"><style type="text/css">body,html {background:#fff;font-family:"Bitstream Vera Sans","Lucida Grande","Lucida Sans Unicode",Lucidux,Verdana,Lucida,sans-serif;}tr:nth-child(even) {background:#f4f4f4;}th,td {padding:0.1em 0.5em;}th {text-align:left;font-weight:bold;background:#eee;border-bottom:1px solid #aaa;}#list {border:1px solid #aaa;width:100%;}a {color:#a33;}a:hover {color:#e33;}</style>
2
3<title>Index of /sources/libxml2/2.10/</title>
4</head><body><h1>Index of /sources/libxml2/2.10/</h1>
5<table id="list"><thead><tr><th style="width:55%"><a href="?C=N&amp;O=A">File Name</a>&nbsp;<a href="?C=N&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:20%"><a href="?C=S&amp;O=A">File Size</a>&nbsp;<a href="?C=S&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:25%"><a href="?C=M&amp;O=A">Date</a>&nbsp;<a href="?C=M&amp;O=D">&nbsp;&darr;&nbsp;</a></th></tr></thead>
6<tbody><tr><td class="link"><a href="../">Parent directory/</a></td><td class="size">-</td><td class="date">-</td></tr>
7<tr><td class="link"><a href="LATEST-IS-2.10.3" title="LATEST-IS-2.10.3">LATEST-IS-2.10.3</a></td><td class="size">2.5 MiB</td><td class="date">2022-Oct-14 12:55</td></tr>
8<tr><td class="link"><a href="libxml2-2.10.0.news" title="libxml2-2.10.0.news">libxml2-2.10.0.news</a></td><td class="size">7.1 KiB</td><td class="date">2022-Aug-17 11:55</td></tr>
9<tr><td class="link"><a href="libxml2-2.10.0.sha256sum" title="libxml2-2.10.0.sha256sum">libxml2-2.10.0.sha256sum</a></td><td class="size">174 B</td><td class="date">2022-Aug-17 11:55</td></tr>
10<tr><td class="link"><a href="libxml2-2.10.0.tar.xz" title="libxml2-2.10.0.tar.xz">libxml2-2.10.0.tar.xz</a></td><td class="size">2.6 MiB</td><td class="date">2022-Aug-17 11:55</td></tr>
11<tr><td class="link"><a href="libxml2-2.10.1.news" title="libxml2-2.10.1.news">libxml2-2.10.1.news</a></td><td class="size">455 B</td><td class="date">2022-Aug-25 11:33</td></tr>
12<tr><td class="link"><a href="libxml2-2.10.1.sha256sum" title="libxml2-2.10.1.sha256sum">libxml2-2.10.1.sha256sum</a></td><td class="size">174 B</td><td class="date">2022-Aug-25 11:33</td></tr>
13<tr><td class="link"><a href="libxml2-2.10.1.tar.xz" title="libxml2-2.10.1.tar.xz">libxml2-2.10.1.tar.xz</a></td><td class="size">2.6 MiB</td><td class="date">2022-Aug-25 11:33</td></tr>
14<tr><td class="link"><a href="libxml2-2.10.2.news" title="libxml2-2.10.2.news">libxml2-2.10.2.news</a></td><td class="size">309 B</td><td class="date">2022-Aug-29 14:56</td></tr>
15<tr><td class="link"><a href="libxml2-2.10.2.sha256sum" title="libxml2-2.10.2.sha256sum">libxml2-2.10.2.sha256sum</a></td><td class="size">174 B</td><td class="date">2022-Aug-29 14:56</td></tr>
16<tr><td class="link"><a href="libxml2-2.10.2.tar.xz" title="libxml2-2.10.2.tar.xz">libxml2-2.10.2.tar.xz</a></td><td class="size">2.5 MiB</td><td class="date">2022-Aug-29 14:56</td></tr>
17<tr><td class="link"><a href="libxml2-2.10.3.news" title="libxml2-2.10.3.news">libxml2-2.10.3.news</a></td><td class="size">294 B</td><td class="date">2022-Oct-14 12:55</td></tr>
18<tr><td class="link"><a href="libxml2-2.10.3.sha256sum" title="libxml2-2.10.3.sha256sum">libxml2-2.10.3.sha256sum</a></td><td class="size">174 B</td><td class="date">2022-Oct-14 12:55</td></tr>
19<tr><td class="link"><a href="libxml2-2.10.3.tar.xz" title="libxml2-2.10.3.tar.xz">libxml2-2.10.3.tar.xz</a></td><td class="size">2.5 MiB</td><td class="date">2022-Oct-14 12:55</td></tr>
20</tbody></table></body></html>
diff --git a/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.9/index.html b/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.9/index.html
new file mode 100644
index 0000000000..abdfdd0fa2
--- /dev/null
+++ b/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/2.9/index.html
@@ -0,0 +1,40 @@
1<!DOCTYPE html><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width"><style type="text/css">body,html {background:#fff;font-family:"Bitstream Vera Sans","Lucida Grande","Lucida Sans Unicode",Lucidux,Verdana,Lucida,sans-serif;}tr:nth-child(even) {background:#f4f4f4;}th,td {padding:0.1em 0.5em;}th {text-align:left;font-weight:bold;background:#eee;border-bottom:1px solid #aaa;}#list {border:1px solid #aaa;width:100%;}a {color:#a33;}a:hover {color:#e33;}</style>
2
3<title>Index of /sources/libxml2/2.9/</title>
4</head><body><h1>Index of /sources/libxml2/2.9/</h1>
5<table id="list"><thead><tr><th style="width:55%"><a href="?C=N&amp;O=A">File Name</a>&nbsp;<a href="?C=N&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:20%"><a href="?C=S&amp;O=A">File Size</a>&nbsp;<a href="?C=S&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:25%"><a href="?C=M&amp;O=A">Date</a>&nbsp;<a href="?C=M&amp;O=D">&nbsp;&darr;&nbsp;</a></th></tr></thead>
6<tbody><tr><td class="link"><a href="../">Parent directory/</a></td><td class="size">-</td><td class="date">-</td></tr>
7<tr><td class="link"><a href="LATEST-IS-2.9.14" title="LATEST-IS-2.9.14">LATEST-IS-2.9.14</a></td><td class="size">3.0 MiB</td><td class="date">2022-May-02 12:03</td></tr>
8<tr><td class="link"><a href="libxml2-2.9.0.sha256sum" title="libxml2-2.9.0.sha256sum">libxml2-2.9.0.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:27</td></tr>
9<tr><td class="link"><a href="libxml2-2.9.0.tar.xz" title="libxml2-2.9.0.tar.xz">libxml2-2.9.0.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-Feb-14 18:27</td></tr>
10<tr><td class="link"><a href="libxml2-2.9.1.sha256sum" title="libxml2-2.9.1.sha256sum">libxml2-2.9.1.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:28</td></tr>
11<tr><td class="link"><a href="libxml2-2.9.1.tar.xz" title="libxml2-2.9.1.tar.xz">libxml2-2.9.1.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-Feb-14 18:28</td></tr>
12<tr><td class="link"><a href="libxml2-2.9.10.sha256sum" title="libxml2-2.9.10.sha256sum">libxml2-2.9.10.sha256sum</a></td><td class="size">88 B</td><td class="date">2022-Feb-14 18:42</td></tr>
13<tr><td class="link"><a href="libxml2-2.9.10.tar.xz" title="libxml2-2.9.10.tar.xz">libxml2-2.9.10.tar.xz</a></td><td class="size">3.2 MiB</td><td class="date">2022-Feb-14 18:42</td></tr>
14<tr><td class="link"><a href="libxml2-2.9.11.sha256sum" title="libxml2-2.9.11.sha256sum">libxml2-2.9.11.sha256sum</a></td><td class="size">88 B</td><td class="date">2022-Feb-14 18:43</td></tr>
15<tr><td class="link"><a href="libxml2-2.9.11.tar.xz" title="libxml2-2.9.11.tar.xz">libxml2-2.9.11.tar.xz</a></td><td class="size">3.2 MiB</td><td class="date">2022-Feb-14 18:43</td></tr>
16<tr><td class="link"><a href="libxml2-2.9.12.sha256sum" title="libxml2-2.9.12.sha256sum">libxml2-2.9.12.sha256sum</a></td><td class="size">88 B</td><td class="date">2022-Feb-14 18:45</td></tr>
17<tr><td class="link"><a href="libxml2-2.9.12.tar.xz" title="libxml2-2.9.12.tar.xz">libxml2-2.9.12.tar.xz</a></td><td class="size">3.2 MiB</td><td class="date">2022-Feb-14 18:45</td></tr>
18<tr><td class="link"><a href="libxml2-2.9.13.news" title="libxml2-2.9.13.news">libxml2-2.9.13.news</a></td><td class="size">26.6 KiB</td><td class="date">2022-Feb-20 12:42</td></tr>
19<tr><td class="link"><a href="libxml2-2.9.13.sha256sum" title="libxml2-2.9.13.sha256sum">libxml2-2.9.13.sha256sum</a></td><td class="size">174 B</td><td class="date">2022-Feb-20 12:42</td></tr>
20<tr><td class="link"><a href="libxml2-2.9.13.tar.xz" title="libxml2-2.9.13.tar.xz">libxml2-2.9.13.tar.xz</a></td><td class="size">3.1 MiB</td><td class="date">2022-Feb-20 12:42</td></tr>
21<tr><td class="link"><a href="libxml2-2.9.14.news" title="libxml2-2.9.14.news">libxml2-2.9.14.news</a></td><td class="size">1.0 KiB</td><td class="date">2022-May-02 12:03</td></tr>
22<tr><td class="link"><a href="libxml2-2.9.14.sha256sum" title="libxml2-2.9.14.sha256sum">libxml2-2.9.14.sha256sum</a></td><td class="size">174 B</td><td class="date">2022-May-02 12:03</td></tr>
23<tr><td class="link"><a href="libxml2-2.9.14.tar.xz" title="libxml2-2.9.14.tar.xz">libxml2-2.9.14.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-May-02 12:03</td></tr>
24<tr><td class="link"><a href="libxml2-2.9.2.sha256sum" title="libxml2-2.9.2.sha256sum">libxml2-2.9.2.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:30</td></tr>
25<tr><td class="link"><a href="libxml2-2.9.2.tar.xz" title="libxml2-2.9.2.tar.xz">libxml2-2.9.2.tar.xz</a></td><td class="size">3.2 MiB</td><td class="date">2022-Feb-14 18:30</td></tr>
26<tr><td class="link"><a href="libxml2-2.9.3.sha256sum" title="libxml2-2.9.3.sha256sum">libxml2-2.9.3.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:31</td></tr>
27<tr><td class="link"><a href="libxml2-2.9.3.tar.xz" title="libxml2-2.9.3.tar.xz">libxml2-2.9.3.tar.xz</a></td><td class="size">3.2 MiB</td><td class="date">2022-Feb-14 18:31</td></tr>
28<tr><td class="link"><a href="libxml2-2.9.4.sha256sum" title="libxml2-2.9.4.sha256sum">libxml2-2.9.4.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:33</td></tr>
29<tr><td class="link"><a href="libxml2-2.9.4.tar.xz" title="libxml2-2.9.4.tar.xz">libxml2-2.9.4.tar.xz</a></td><td class="size">2.9 MiB</td><td class="date">2022-Feb-14 18:33</td></tr>
30<tr><td class="link"><a href="libxml2-2.9.5.sha256sum" title="libxml2-2.9.5.sha256sum">libxml2-2.9.5.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:35</td></tr>
31<tr><td class="link"><a href="libxml2-2.9.5.tar.xz" title="libxml2-2.9.5.tar.xz">libxml2-2.9.5.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-Feb-14 18:35</td></tr>
32<tr><td class="link"><a href="libxml2-2.9.6.sha256sum" title="libxml2-2.9.6.sha256sum">libxml2-2.9.6.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:36</td></tr>
33<tr><td class="link"><a href="libxml2-2.9.6.tar.xz" title="libxml2-2.9.6.tar.xz">libxml2-2.9.6.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-Feb-14 18:36</td></tr>
34<tr><td class="link"><a href="libxml2-2.9.7.sha256sum" title="libxml2-2.9.7.sha256sum">libxml2-2.9.7.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:37</td></tr>
35<tr><td class="link"><a href="libxml2-2.9.7.tar.xz" title="libxml2-2.9.7.tar.xz">libxml2-2.9.7.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-Feb-14 18:37</td></tr>
36<tr><td class="link"><a href="libxml2-2.9.8.sha256sum" title="libxml2-2.9.8.sha256sum">libxml2-2.9.8.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:39</td></tr>
37<tr><td class="link"><a href="libxml2-2.9.8.tar.xz" title="libxml2-2.9.8.tar.xz">libxml2-2.9.8.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-Feb-14 18:39</td></tr>
38<tr><td class="link"><a href="libxml2-2.9.9.sha256sum" title="libxml2-2.9.9.sha256sum">libxml2-2.9.9.sha256sum</a></td><td class="size">87 B</td><td class="date">2022-Feb-14 18:40</td></tr>
39<tr><td class="link"><a href="libxml2-2.9.9.tar.xz" title="libxml2-2.9.9.tar.xz">libxml2-2.9.9.tar.xz</a></td><td class="size">3.0 MiB</td><td class="date">2022-Feb-14 18:40</td></tr>
40</tbody></table></body></html>
diff --git a/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/index.html b/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/index.html
new file mode 100644
index 0000000000..c183e06a55
--- /dev/null
+++ b/bitbake/lib/bb/tests/fetch-testdata/software/libxml2/index.html
@@ -0,0 +1,19 @@
1<!DOCTYPE html><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width"><style type="text/css">body,html {background:#fff;font-family:"Bitstream Vera Sans","Lucida Grande","Lucida Sans Unicode",Lucidux,Verdana,Lucida,sans-serif;}tr:nth-child(even) {background:#f4f4f4;}th,td {padding:0.1em 0.5em;}th {text-align:left;font-weight:bold;background:#eee;border-bottom:1px solid #aaa;}#list {border:1px solid #aaa;width:100%;}a {color:#a33;}a:hover {color:#e33;}</style>
2
3<title>Index of /sources/libxml2/</title>
4</head><body><h1>Index of /sources/libxml2/</h1>
5<table id="list"><thead><tr><th style="width:55%"><a href="?C=N&amp;O=A">File Name</a>&nbsp;<a href="?C=N&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:20%"><a href="?C=S&amp;O=A">File Size</a>&nbsp;<a href="?C=S&amp;O=D">&nbsp;&darr;&nbsp;</a></th><th style="width:25%"><a href="?C=M&amp;O=A">Date</a>&nbsp;<a href="?C=M&amp;O=D">&nbsp;&darr;&nbsp;</a></th></tr></thead>
6<tbody><tr><td class="link"><a href="../">Parent directory/</a></td><td class="size">-</td><td class="date">-</td></tr>
7<tr><td class="link"><a href="2.0/" title="2.0">2.0/</a></td><td class="size">-</td><td class="date">2009-Jul-14 13:04</td></tr>
8<tr><td class="link"><a href="2.1/" title="2.1">2.1/</a></td><td class="size">-</td><td class="date">2009-Jul-14 13:04</td></tr>
9<tr><td class="link"><a href="2.10/" title="2.10">2.10/</a></td><td class="size">-</td><td class="date">2022-Oct-14 12:55</td></tr>
10<tr><td class="link"><a href="2.2/" title="2.2">2.2/</a></td><td class="size">-</td><td class="date">2009-Jul-14 13:04</td></tr>
11<tr><td class="link"><a href="2.3/" title="2.3">2.3/</a></td><td class="size">-</td><td class="date">2009-Jul-14 13:05</td></tr>
12<tr><td class="link"><a href="2.4/" title="2.4">2.4/</a></td><td class="size">-</td><td class="date">2009-Jul-14 13:05</td></tr>
13<tr><td class="link"><a href="2.5/" title="2.5">2.5/</a></td><td class="size">-</td><td class="date">2009-Jul-14 13:05</td></tr>
14<tr><td class="link"><a href="2.6/" title="2.6">2.6/</a></td><td class="size">-</td><td class="date">2009-Jul-14 13:05</td></tr>
15<tr><td class="link"><a href="2.7/" title="2.7">2.7/</a></td><td class="size">-</td><td class="date">2022-Feb-14 18:24</td></tr>
16<tr><td class="link"><a href="2.8/" title="2.8">2.8/</a></td><td class="size">-</td><td class="date">2022-Feb-14 18:26</td></tr>
17<tr><td class="link"><a href="2.9/" title="2.9">2.9/</a></td><td class="size">-</td><td class="date">2022-May-02 12:04</td></tr>
18<tr><td class="link"><a href="cache.json" title="cache.json">cache.json</a></td><td class="size">22.8 KiB</td><td class="date">2022-Oct-14 12:55</td></tr>
19</tbody></table></body></html>
diff --git a/bitbake/lib/bb/tests/fetch-testdata/software/miniupnp/download.php b/bitbake/lib/bb/tests/fetch-testdata/software/miniupnp/download.php
new file mode 100644
index 0000000000..e27ee134f2
--- /dev/null
+++ b/bitbake/lib/bb/tests/fetch-testdata/software/miniupnp/download.php
@@ -0,0 +1,3528 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5<head>
6<title>MiniUPnP download zone</title>
7<link href="../css/miniupnp.css" rel="stylesheet" type="text/css"/>
8<meta name="description" content="files download of the miniupnp project"/>
9<meta name="keywords" content="upnp,download,openbsd,freebsd,linux,windows"/>
10<meta name="viewport" content="width=device-width" />
11<link href="rss.php" title="MiniUPnPd, MiniUPnPc and MiniSSDPd Files" type="application/rss+xml" rel="alternate" />
12<link rel="canonical" href="http://miniupnp.free.fr/files/" />
13<link rel="alternate" hreflang="fr" href="/files/index_fr.php" />
14<script async="async" src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" type="text/javascript"></script>
15<script type="text/javascript">
16 (adsbygoogle = window.adsbygoogle || []).push({
17 google_ad_client: "ca-pub-6883148866513192",
18 enable_page_level_ads: true
19 });
20</script>
21</head>
22<body>
23<h2>MiniUPnP Project</h2>
24
25<p align="center">
26<a href="../">Home</a> |
27<b>Downloads</b> |
28<a href="../devicelist.php">Compatibility list</a> |
29<a href="../libnatpmp.html">libnatpmp</a> |
30<a href="../minissdpd.html">MiniSSDPd</a> |
31<a href="../xchat-upnp.html">xchat upnp patch</a> |
32<a href="../search.html">Search</a> |
33<a href="https://miniupnp.tuxfamily.org/forum/">Forum</a>
34</p>
35<p align="center">
36<b>English</b> | <a href="/files/index_fr.php">Fran&ccedil;ais</a>
37</p>
38
39<div align="center">
40<script type="text/javascript"><!--
41google_ad_client = "pub-6883148866513192";
42/* 728x90, created 7/10/08 */
43google_ad_slot = "0774293141";
44google_ad_width = 728;
45google_ad_height = 90;
46//-->
47</script>
48<script type="text/javascript"
49src="https://pagead2.googlesyndication.com/pagead/show_ads.js">
50</script>
51</div>
52
53<h2>MiniUPnP download zone</h2>
54<p>
55Find on this page the source of miniupnp and
56some related files. You will also find precompiled binaries
57of the UPnP client sample program for windows compiled using
58<a href="https://mingw.osdn.io/">MinGW</a>. There are also Windows
59binaries (including python module) automatically built using
60<a href="https://ci.appveyor.com/project/miniupnp/miniupnp/build/artifacts">AppVeyor</a>.
61</p>
62<p>If you just need one of the software installed on your machine,
63you probably don't need to download and compile the source files.
64It is very likely that a package/port already exists for
65your system/distribution. Refer to your system documentation
66to find how to search and install a package/port.
67Mac OS X does have port systems too : see
68<a href="http://www.macports.org/">MacPorts</a> or
69<a href="http://mxcl.github.com/homebrew/">Homebrew</a> or
70<a href="http://www.finkproject.org/">Fink</a>.
71</p>
72<p>
73The miniupnpc (client) sources have been successfully compiled
74under Windows XP/vista/7/10/etc. (using
75<a href="https://mingw.osdn.io/">MinGW</a>,
76<a href="https://www.mingw-w64.org/">Mingw-w64</a>
77or <a href="http://www.cygwin.com/">Cygwin</a>),
78Linux, OpenBSD, FreeBSD, NetBSD, DragonFlyBSD,
79Solaris, MacOS X and AmigaOS. <br/>
80The Makefile of the client is made for GNU make :
81check which version your system have
82with the command "make --version". On some systems, such as OpenBSD,
83you have to use "gmake". Under Windows with MinGW, GNU make is
84called "mingw32-make" and a sligthly modified version of the Makefile
85should be used : Makefile.mingw. Run "mingw32make.bat" to compile. <br/>
86If you have any compatibility problem, please post on the
87<a href="https://miniupnp.tuxfamily.org/forum/">forum</a>
88or contact me by email.
89</p>
90<!--
91<p>A devoted user compiled miniupnp<strong>c</strong> for
92Openwrt (currently Kamikaze 7.09)
93and his work is available here :
94<a href="http://replay.waybackmachine.org/20081120030628/http://www.myantihero.net/pub/openwrt/packages/">http://myantihero.net/pub/openwrt/packages/</a>.</p>
95-->
96<p>Get miniupnpc under AmigaOS 4 on
97<a href="http://os4depot.net/index.php?function=showfile&amp;file=network/misc/miniupnpc.lha">OS4Depot</a>.
98</p>
99<p>
100Dario Meloni has made a Ruby Gem embedding miniupnpc :
101<a href="https://rubygems.org/gems/mupnp">https://rubygems.org/gems/mupnp</a>.
102</p>
103<p>
104The python module is available on pypi.org :
105<a href="https://pypi.org/project/miniupnpc/">pip install miniupnpc</a>.
106</p>
107<p>
108The daemon (starting in November 2006) compiles with BSD make under BSD
109and Solaris.<br/>
110To compile the daemon under linux, use "make -f Makefile.linux"<br/>
111To compile for <a href="http://openwrt.org/">OpenWRT</a>
112please read the README.openwrt file, or use the packages
113<a href="https://openwrt.org/packages/pkgdata/miniupnpd">miniupnpc</a> and
114<a href="https://openwrt.org/packages/pkgdata/miniupnpd">miniupnpd</a>.
115<!-- The
116<a href="http://www.x-wrt.org/">X-Wrt</a> project is providing
117precompiled ipkg packages for OpenWrt for both OpenWrt
118<a href="ftp://ftp.berlios.de/pub/xwrt/packages/">White Russian</a>
119and OpenWrt
120<a href="ftp://ftp.berlios.de/pub/xwrt/kamikaze/packages">kamikaze</a>.
121Check
122<a href="ftp://ftp.berlios.de/pub/xwrt/">ftp://ftp.berlios.de/pub/xwrt/</a>.
123For White Russian, take a look at
124<a href="http://jackassofalltrades.com/openwrt/">this</a>. -->
125<br/>
126<a href="http://pfsense.com">pfSense</a> users are advised to use the
127miniupnpd port available for their system. Recent versions of
128pfSense include MiniUPnPd in the base system.
129<br/>
130For <a href="http://en.wikipedia.org/wiki/WRT54G">Linksys WRT54G</a>
131and WRT54GL owners,
132<a href="http://sourceforge.net/projects/tarifa/">Tarifa firmware</a>
133is another alternative to get miniUPnPd running on the router.
134</p>
135<p>
136Please read README and
137LICENCE files included with the distribution for further informations.
138</p>
139<p>
140The MiniUPnP daemon (miniupnpd) is working under
141<a href="http://www.openbsd.org/">OpenBSD</a>,
142<a href="http://www.netbsd.org/">NetBSD</a>,
143<a href="http://www.freebsd.org/">FreeBSD</a>,
144<a href="http://www.dragonflybsd.org/">DragonFlyBSD</a>,
145<a href="http://www.apple.com/macosx/">Mac OS X</a> and
146(<a href="https://en.wikipedia.org/wiki/OpenSolaris">Open</a>)<a href="http://www.oracle.com/us/products/servers-storage/solaris/solaris11/overview/index.html">Solaris</a>
147with <a href="http://www.openbsd.org/faq/pf/">pf</a>,
148with <a href="https://en.wikipedia.org/wiki/IPFilter">IP Filter</a> or
149with <a href="http://en.wikipedia.org/wiki/Ipfirewall">ipfw</a>.
150The linux version uses either libiptc which permits to access
151<a href="http://netfilter.org/">netfilter</a>
152rules inside the kernel the same way as
153<a href="https://www.netfilter.org/projects/iptables/index.html">iptables</a>, or
154<a href="https://www.netfilter.org/projects/libnftnl/index.html">libnftnl</a>
155which is the equivalent for
156<a href="https://www.netfilter.org/projects/nftables/index.html">nftables</a>.
157</p>
158
159<p>Releases are now GPG signed with the key <a href="../A31ACAAF.asc">A31ACAAF</a>.
160Previous signing key was <a href="../A5C0863C.asc">A5C0863C</a>.
161Get it from your favorite
162<a href="https://pgp.mit.edu/pks/lookup?search=0xA31ACAAF&amp;op=index&amp;fingerprint=on">key server</a>.</p>
163
164<h4>REST API</h4>
165<p>You can use the REST API to get the latest releases available:</p>
166<ul>
167<li><a href="rest.php/tags/miniupnpd?count=1">rest.php/tags/miniupnpd?count=1</a>: latest miniupnpd.</li>
168<li><a href="rest.php/tags?count=1">rest.php/tags?count=1</a>: miniupnpc, miniupnpd and minissdpd.</li>
169</ul>
170
171<h4>You can help !</h4>
172<p>If you make a package/port for your favorite OS distribution,
173inform me so I can upload the package here or add a link to your
174repository.
175</p>
176
177<h4>Latest files</h4>
178<table>
179<tr><th>name</th>
180<th>size</th>
181<th>date</th>
182<th>comment</th>
183<th><!-- Changelog --></th>
184<th><!-- Signature --></th>
185</tr>
186<tr>
187 <td class="filename"><a href='miniupnpc-2.3.2.tar.gz'>miniupnpc-2.3.2.tar.gz</a></td>
188 <td class="filesize">140137</td>
189 <td class="filedate">05/03/2025 10:31</td>
190 <td class="comment">MiniUPnP client release source code</td>
191 <td><a href="changelog.php?file=miniupnpc-2.3.2.tar.gz">changelog</a></td>
192 <td><a href="miniupnpc-2.3.2.tar.gz.sig">Signature</a></td>
193</tr>
194<tr>
195 <td class="filename"><a href='miniupnpd-2.3.7.tar.gz'>miniupnpd-2.3.7.tar.gz</a></td>
196 <td class="filesize">265329</td>
197 <td class="filedate">22/06/2024 22:31</td>
198 <td class="comment">MiniUPnP daemon release source code</td>
199 <td><a href="changelog.php?file=miniupnpd-2.3.7.tar.gz">changelog</a></td>
200 <td><a href="miniupnpd-2.3.7.tar.gz.sig">Signature</a></td>
201</tr>
202<tr>
203 <td class="filename"><a href='libnatpmp-20230423.tar.gz'>libnatpmp-20230423.tar.gz</a></td>
204 <td class="filesize">26506</td>
205 <td class="filedate">23/04/2023 11:02</td>
206 <td class="comment">latest libnatpmp source code</td>
207 <td><a href="changelog.php?file=libnatpmp-20230423.tar.gz">changelog</a></td>
208 <td><a href="libnatpmp-20230423.tar.gz.sig">Signature</a></td>
209</tr>
210<tr>
211 <td class="filename"><a href='minissdpd-1.6.0.tar.gz'>minissdpd-1.6.0.tar.gz</a></td>
212 <td class="filesize">39077</td>
213 <td class="filedate">22/10/2022 18:41</td>
214 <td class="comment">MiniSSDPd release source code</td>
215 <td><a href="changelog.php?file=minissdpd-1.6.0.tar.gz">changelog</a></td>
216 <td><a href="minissdpd-1.6.0.tar.gz.sig">Signature</a></td>
217</tr>
218<tr>
219 <td class="filename"><a href='upnpc-exe-win32-20220515.zip'>upnpc-exe-win32-20220515.zip</a></td>
220 <td class="filesize">69503</td>
221 <td class="filedate">15/05/2022 14:31</td>
222 <td class="comment">Windows executable</td>
223 <td><a href="changelog.php?file=upnpc-exe-win32-20220515.zip">changelog</a></td>
224 <td></td>
225</tr>
226<tr>
227 <td class="filename"><a href='minissdpd-1.5.20211105.tar.gz'>minissdpd-1.5.20211105.tar.gz</a></td>
228 <td class="filesize">38870</td>
229 <td class="filedate">04/11/2021 23:34</td>
230 <td class="comment">latest MiniSSDPd source code</td>
231 <td><a href="changelog.php?file=minissdpd-1.5.20211105.tar.gz">changelog</a></td>
232 <td><a href="minissdpd-1.5.20211105.tar.gz.sig">Signature</a></td>
233</tr>
234<tr>
235 <td class="filename"><a href='miniupnpc-2.1.20201016.tar.gz'>miniupnpc-2.1.20201016.tar.gz</a></td>
236 <td class="filesize">97682</td>
237 <td class="filedate">15/10/2020 22:31</td>
238 <td class="comment">latest MiniUPnP client source code</td>
239 <td><a href="changelog.php?file=miniupnpc-2.1.20201016.tar.gz">changelog</a></td>
240 <td><a href="miniupnpc-2.1.20201016.tar.gz.sig">Signature</a></td>
241</tr>
242<tr>
243 <td class="filename"><a href='miniupnpd-2.1.20200510.tar.gz'>miniupnpd-2.1.20200510.tar.gz</a></td>
244 <td class="filesize">245426</td>
245 <td class="filedate">10/05/2020 18:23</td>
246 <td class="comment">latest MiniUPnP daemon source code</td>
247 <td><a href="changelog.php?file=miniupnpd-2.1.20200510.tar.gz">changelog</a></td>
248 <td><a href="miniupnpd-2.1.20200510.tar.gz.sig">Signature</a></td>
249</tr>
250<tr>
251 <td class="filename"><a href='xchat-upnp20110811.patch'>xchat-upnp20110811.patch</a></td>
252 <td class="filesize">10329</td>
253 <td class="filedate">11/08/2011 15:18</td>
254 <td class="comment">Patch to add UPnP capabilities to xchat</td>
255 <td><a href="changelog.php?file=xchat-upnp20110811.patch">changelog</a></td>
256 <td></td>
257</tr>
258<tr>
259 <td class="filename"><a href='minidlna_1.0.21.minissdp1.patch'>minidlna_1.0.21.minissdp1.patch</a></td>
260 <td class="filesize">7598</td>
261 <td class="filedate">25/07/2011 14:57</td>
262 <td class="comment">Patch for MiniDLNA to use miniSSDPD</td>
263 <td><a href="changelog.php?file=minidlna_1.0.21.minissdp1.patch">changelog</a></td>
264 <td></td>
265</tr>
266<tr>
267 <td class="filename"><a href='miniupnpc-new20060630.tar.gz'>miniupnpc-new20060630.tar.gz</a></td>
268 <td class="filesize">14840</td>
269 <td class="filedate">04/11/2006 18:16</td>
270 <td class="comment">Jo&atilde;o Paulo Barraca version of the upnp client</td>
271 <td><a href="changelog.php?file=miniupnpc-new20060630.tar.gz">changelog</a></td>
272 <td></td>
273</tr>
274</table>
275
276<h4>All files</h4>
277<table>
278<tr><th>name</th>
279<th>size</th>
280<th>date</th>
281<th>comment</th>
282<th><!-- signature --></th>
283</tr>
284<tr>
285 <td class="filename"><a href='download.php?file=miniupnpc-2.3.2.tar.gz'>miniupnpc-2.3.2.tar.gz</a></td>
286 <td class="filesize">140137</td>
287 <td class="filedate">05/03/2025 10:31:36 +0000</td>
288 <td class="comment">MiniUPnP client release source code</td>
289 <td><a href="miniupnpc-2.3.2.tar.gz.sig">Signature</a></td>
290</tr>
291<tr>
292 <td class="filename"><a href='download.php?file=miniupnpc-2.3.1.tar.gz'>miniupnpc-2.3.1.tar.gz</a></td>
293 <td class="filesize">139499</td>
294 <td class="filedate">23/02/2025 16:44:16 +0000</td>
295 <td class="comment">MiniUPnP client release source code</td>
296 <td><a href="miniupnpc-2.3.1.tar.gz.sig">Signature</a></td>
297</tr>
298<tr>
299 <td class="filename"><a href='download.php?file=miniupnpc-2.3.0.tar.gz'>miniupnpc-2.3.0.tar.gz</a></td>
300 <td class="filesize">105071</td>
301 <td class="filedate">10/01/2025 23:16:45 +0000</td>
302 <td class="comment">MiniUPnP client release source code</td>
303 <td><a href="miniupnpc-2.3.0.tar.gz.sig">Signature</a></td>
304</tr>
305<tr>
306 <td class="filename"><a href='download.php?file=miniupnpd-2.3.7.tar.gz'>miniupnpd-2.3.7.tar.gz</a></td>
307 <td class="filesize">265329</td>
308 <td class="filedate">22/06/2024 22:31:38 +0000</td>
309 <td class="comment">MiniUPnP daemon release source code</td>
310 <td><a href="miniupnpd-2.3.7.tar.gz.sig">Signature</a></td>
311</tr>
312<tr>
313 <td class="filename"><a href='download.php?file=miniupnpc-2.2.8.tar.gz'>miniupnpc-2.2.8.tar.gz</a></td>
314 <td class="filesize">104603</td>
315 <td class="filedate">08/06/2024 22:13:39 +0000</td>
316 <td class="comment">MiniUPnP client release source code</td>
317 <td><a href="miniupnpc-2.2.8.tar.gz.sig">Signature</a></td>
318</tr>
319<tr>
320 <td class="filename"><a href='download.php?file=miniupnpd-2.3.6.tar.gz'>miniupnpd-2.3.6.tar.gz</a></td>
321 <td class="filesize">263018</td>
322 <td class="filedate">19/03/2024 23:39:51 +0000</td>
323 <td class="comment">MiniUPnP daemon release source code</td>
324 <td><a href="miniupnpd-2.3.6.tar.gz.sig">Signature</a></td>
325</tr>
326<tr>
327 <td class="filename"><a href='download.php?file=miniupnpc-2.2.7.tar.gz'>miniupnpc-2.2.7.tar.gz</a></td>
328 <td class="filesize">104258</td>
329 <td class="filedate">19/03/2024 23:25:18 +0000</td>
330 <td class="comment">MiniUPnP client release source code</td>
331 <td><a href="miniupnpc-2.2.7.tar.gz.sig">Signature</a></td>
332</tr>
333<tr>
334 <td class="filename"><a href='download.php?file=miniupnpd-2.3.5.tar.gz'>miniupnpd-2.3.5.tar.gz</a></td>
335 <td class="filesize">261952</td>
336 <td class="filedate">02/03/2024 11:04:07 +0000</td>
337 <td class="comment">MiniUPnP daemon release source code</td>
338 <td><a href="miniupnpd-2.3.5.tar.gz.sig">Signature</a></td>
339</tr>
340<tr>
341 <td class="filename"><a href='download.php?file=miniupnpd-2.3.4.tar.gz'>miniupnpd-2.3.4.tar.gz</a></td>
342 <td class="filesize">260810</td>
343 <td class="filedate">04/01/2024 00:53:17 +0000</td>
344 <td class="comment">MiniUPnP daemon release source code</td>
345 <td><a href="miniupnpd-2.3.4.tar.gz.sig">Signature</a></td>
346</tr>
347<tr>
348 <td class="filename"><a href='download.php?file=miniupnpc-2.2.6.tar.gz'>miniupnpc-2.2.6.tar.gz</a></td>
349 <td class="filesize">103949</td>
350 <td class="filedate">04/01/2024 00:27:14 +0000</td>
351 <td class="comment">MiniUPnP client release source code</td>
352 <td><a href="miniupnpc-2.2.6.tar.gz.sig">Signature</a></td>
353</tr>
354<tr>
355 <td class="filename"><a href='download.php?file=miniupnpc-2.2.5.tar.gz'>miniupnpc-2.2.5.tar.gz</a></td>
356 <td class="filesize">103654</td>
357 <td class="filedate">11/06/2023 23:14:56 +0000</td>
358 <td class="comment">MiniUPnP client release source code</td>
359 <td><a href="miniupnpc-2.2.5.tar.gz.sig">Signature</a></td>
360</tr>
361<tr>
362 <td class="filename"><a href='download.php?file=libnatpmp-20230423.tar.gz'>libnatpmp-20230423.tar.gz</a></td>
363 <td class="filesize">26506</td>
364 <td class="filedate">23/04/2023 11:02:09 +0000</td>
365 <td class="comment">libnatpmp source code</td>
366 <td><a href="libnatpmp-20230423.tar.gz.sig">Signature</a></td>
367</tr>
368<tr>
369 <td class="filename"><a href='download.php?file=miniupnpd-2.3.3.tar.gz'>miniupnpd-2.3.3.tar.gz</a></td>
370 <td class="filesize">260079</td>
371 <td class="filedate">17/02/2023 03:07:46 +0000</td>
372 <td class="comment">MiniUPnP daemon release source code</td>
373 <td><a href="miniupnpd-2.3.3.tar.gz.sig">Signature</a></td>
374</tr>
375<tr>
376 <td class="filename"><a href='download.php?file=miniupnpd-2.3.2.tar.gz'>miniupnpd-2.3.2.tar.gz</a></td>
377 <td class="filesize">259686</td>
378 <td class="filedate">19/01/2023 23:18:08 +0000</td>
379 <td class="comment">MiniUPnP daemon release source code</td>
380 <td><a href="miniupnpd-2.3.2.tar.gz.sig">Signature</a></td>
381</tr>
382<tr>
383 <td class="filename"><a href='download.php?file=minissdpd-1.6.0.tar.gz'>minissdpd-1.6.0.tar.gz</a></td>
384 <td class="filesize">39077</td>
385 <td class="filedate">22/10/2022 18:41:54 +0000</td>
386 <td class="comment">MiniSSDPd release source code</td>
387 <td><a href="minissdpd-1.6.0.tar.gz.sig">Signature</a></td>
388</tr>
389<tr>
390 <td class="filename"><a href='download.php?file=miniupnpc-2.2.4.tar.gz'>miniupnpc-2.2.4.tar.gz</a></td>
391 <td class="filesize">102932</td>
392 <td class="filedate">21/10/2022 21:01:01 +0000</td>
393 <td class="comment">MiniUPnP client release source code</td>
394 <td><a href="miniupnpc-2.2.4.tar.gz.sig">Signature</a></td>
395</tr>
396<tr>
397 <td class="filename"><a href='download.php?file=miniupnpd-2.3.1.tar.gz'>miniupnpd-2.3.1.tar.gz</a></td>
398 <td class="filesize">258050</td>
399 <td class="filedate">16/10/2022 05:58:44 +0000</td>
400 <td class="comment">MiniUPnP daemon release source code</td>
401 <td><a href="miniupnpd-2.3.1.tar.gz.sig">Signature</a></td>
402</tr>
403<tr>
404 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20220515.zip'>upnpc-exe-win32-20220515.zip</a></td>
405 <td class="filesize">69503</td>
406 <td class="filedate">15/05/2022 14:31:25 +0000</td>
407 <td class="comment">Windows executable</td>
408 <td></td>
409</tr>
410<tr>
411 <td class="filename"><a href='download.php?file=hexchat-2.16.patch'>hexchat-2.16.patch</a></td>
412 <td class="filesize">8147</td>
413 <td class="filedate">19/03/2022 16:52:05 +0000</td>
414 <td class="comment"></td>
415 <td></td>
416</tr>
417<tr>
418 <td class="filename"><a href='download.php?file=miniupnpd-2.3.0.tar.gz'>miniupnpd-2.3.0.tar.gz</a></td>
419 <td class="filesize">256069</td>
420 <td class="filedate">23/01/2022 00:23:32 +0000</td>
421 <td class="comment">MiniUPnP daemon release source code</td>
422 <td><a href="miniupnpd-2.3.0.tar.gz.sig">Signature</a></td>
423</tr>
424<tr>
425 <td class="filename"><a href='download.php?file=minissdpd-1.5.20211105.tar.gz'>minissdpd-1.5.20211105.tar.gz</a></td>
426 <td class="filesize">38870</td>
427 <td class="filedate">04/11/2021 23:34:49 +0000</td>
428 <td class="comment">MiniSSDPd source code</td>
429 <td><a href="minissdpd-1.5.20211105.tar.gz.sig">Signature</a></td>
430</tr>
431<tr>
432 <td class="filename"><a href='download.php?file=miniupnpc-2.2.3.tar.gz'>miniupnpc-2.2.3.tar.gz</a></td>
433 <td class="filesize">101360</td>
434 <td class="filedate">28/09/2021 21:43:32 +0000</td>
435 <td class="comment">MiniUPnP client release source code</td>
436 <td><a href="miniupnpc-2.2.3.tar.gz.sig">Signature</a></td>
437</tr>
438<tr>
439 <td class="filename"><a href='download.php?file=miniupnpd-2.2.3.tar.gz'>miniupnpd-2.2.3.tar.gz</a></td>
440 <td class="filesize">254752</td>
441 <td class="filedate">21/08/2021 08:35:13 +0000</td>
442 <td class="comment">MiniUPnP daemon release source code</td>
443 <td><a href="miniupnpd-2.2.3.tar.gz.sig">Signature</a></td>
444</tr>
445<tr>
446 <td class="filename"><a href='download.php?file=miniupnpd-2.2.2.tar.gz'>miniupnpd-2.2.2.tar.gz</a></td>
447 <td class="filesize">250649</td>
448 <td class="filedate">13/05/2021 11:30:11 +0000</td>
449 <td class="comment">MiniUPnP daemon release source code</td>
450 <td><a href="miniupnpd-2.2.2.tar.gz.sig">Signature</a></td>
451</tr>
452<tr>
453 <td class="filename"><a href='download.php?file=miniupnpc-2.2.2.tar.gz'>miniupnpc-2.2.2.tar.gz</a></td>
454 <td class="filesize">100008</td>
455 <td class="filedate">02/03/2021 23:44:52 +0000</td>
456 <td class="comment">MiniUPnP client release source code</td>
457 <td><a href="miniupnpc-2.2.2.tar.gz.sig">Signature</a></td>
458</tr>
459<tr>
460 <td class="filename"><a href='download.php?file=miniupnpd-2.2.1.tar.gz'>miniupnpd-2.2.1.tar.gz</a></td>
461 <td class="filesize">250023</td>
462 <td class="filedate">20/12/2020 18:08:08 +0000</td>
463 <td class="comment">MiniUPnP daemon release source code</td>
464 <td><a href="miniupnpd-2.2.1.tar.gz.sig">Signature</a></td>
465</tr>
466<tr>
467 <td class="filename"><a href='download.php?file=miniupnpc-2.2.1.tar.gz'>miniupnpc-2.2.1.tar.gz</a></td>
468 <td class="filesize">99595</td>
469 <td class="filedate">20/12/2020 18:08:02 +0000</td>
470 <td class="comment">MiniUPnP client release source code</td>
471 <td><a href="miniupnpc-2.2.1.tar.gz.sig">Signature</a></td>
472</tr>
473<tr>
474 <td class="filename"><a href='download.php?file=miniupnpc-2.2.0.tar.gz'>miniupnpc-2.2.0.tar.gz</a></td>
475 <td class="filesize">98348</td>
476 <td class="filedate">09/11/2020 19:51:24 +0000</td>
477 <td class="comment">MiniUPnP client release source code</td>
478 <td><a href="miniupnpc-2.2.0.tar.gz.sig">Signature</a></td>
479</tr>
480<tr>
481 <td class="filename"><a href='download.php?file=miniupnpd-2.2.0.tar.gz'>miniupnpd-2.2.0.tar.gz</a></td>
482 <td class="filesize">249858</td>
483 <td class="filedate">31/10/2020 09:20:59 +0000</td>
484 <td class="comment">MiniUPnP daemon release source code</td>
485 <td><a href="miniupnpd-2.2.0.tar.gz.sig">Signature</a></td>
486</tr>
487<tr>
488 <td class="filename"><a href='download.php?file=miniupnpd-2.2.0-RC3.tar.gz'>miniupnpd-2.2.0-RC3.tar.gz</a></td>
489 <td class="filesize">249879</td>
490 <td class="filedate">30/10/2020 21:49:49 +0000</td>
491 <td class="comment">MiniUPnP daemon release source code</td>
492 <td><a href="miniupnpd-2.2.0-RC3.tar.gz.sig">Signature</a></td>
493</tr>
494<tr>
495 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20201016.tar.gz'>miniupnpc-2.1.20201016.tar.gz</a></td>
496 <td class="filesize">97682</td>
497 <td class="filedate">15/10/2020 22:31:09 +0000</td>
498 <td class="comment">MiniUPnP client source code</td>
499 <td><a href="miniupnpc-2.1.20201016.tar.gz.sig">Signature</a></td>
500</tr>
501<tr>
502 <td class="filename"><a href='download.php?file=miniupnpd-2.2.0-RC2.tar.gz'>miniupnpd-2.2.0-RC2.tar.gz</a></td>
503 <td class="filesize">248756</td>
504 <td class="filedate">28/09/2020 21:57:22 +0000</td>
505 <td class="comment">MiniUPnP daemon release source code</td>
506 <td><a href="miniupnpd-2.2.0-RC2.tar.gz.sig">Signature</a></td>
507</tr>
508<tr>
509 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20200928.tar.gz'>miniupnpc-2.1.20200928.tar.gz</a></td>
510 <td class="filesize">96508</td>
511 <td class="filedate">28/09/2020 21:56:09 +0000</td>
512 <td class="comment">MiniUPnP client source code</td>
513 <td><a href="miniupnpc-2.1.20200928.tar.gz.sig">Signature</a></td>
514</tr>
515<tr>
516 <td class="filename"><a href='download.php?file=minissdpd-1.5.20200928.tar.gz'>minissdpd-1.5.20200928.tar.gz</a></td>
517 <td class="filesize">37860</td>
518 <td class="filedate">28/09/2020 21:55:40 +0000</td>
519 <td class="comment">MiniSSDPd source code</td>
520 <td><a href="minissdpd-1.5.20200928.tar.gz.sig">Signature</a></td>
521</tr>
522<tr>
523 <td class="filename"><a href='download.php?file=miniupnpd-2.2.0-RC1.tar.gz'>miniupnpd-2.2.0-RC1.tar.gz</a></td>
524 <td class="filesize">247772</td>
525 <td class="filedate">06/06/2020 18:34:50 +0000</td>
526 <td class="comment">MiniUPnP daemon release source code</td>
527 <td><a href="miniupnpd-2.2.0-RC1.tar.gz.sig">Signature</a></td>
528</tr>
529<tr>
530 <td class="filename"><a href='download.php?file=miniupnpd-2.2.0-RC0.tar.gz'>miniupnpd-2.2.0-RC0.tar.gz</a></td>
531 <td class="filesize">245507</td>
532 <td class="filedate">16/05/2020 18:03:17 +0000</td>
533 <td class="comment">MiniUPnP daemon release source code</td>
534 <td><a href="miniupnpd-2.2.0-RC0.tar.gz.sig">Signature</a></td>
535</tr>
536<tr>
537 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20200510.tar.gz'>miniupnpd-2.1.20200510.tar.gz</a></td>
538 <td class="filesize">245426</td>
539 <td class="filedate">10/05/2020 18:23:13 +0000</td>
540 <td class="comment">MiniUPnP daemon source code</td>
541 <td><a href="miniupnpd-2.1.20200510.tar.gz.sig">Signature</a></td>
542</tr>
543<tr>
544 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20200329.tar.gz'>miniupnpd-2.1.20200329.tar.gz</a></td>
545 <td class="filesize">243725</td>
546 <td class="filedate">29/03/2020 09:11:02 +0000</td>
547 <td class="comment">MiniUPnP daemon source code</td>
548 <td><a href="miniupnpd-2.1.20200329.tar.gz.sig">Signature</a></td>
549</tr>
550<tr>
551 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20191224.tar.gz'>miniupnpc-2.1.20191224.tar.gz</a></td>
552 <td class="filesize">94740</td>
553 <td class="filedate">23/12/2019 23:37:32 +0000</td>
554 <td class="comment">MiniUPnP client source code</td>
555 <td><a href="miniupnpc-2.1.20191224.tar.gz.sig">Signature</a></td>
556</tr>
557<tr>
558 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20191006.tar.gz'>miniupnpd-2.1.20191006.tar.gz</a></td>
559 <td class="filesize">243255</td>
560 <td class="filedate">06/10/2019 21:02:31 +0000</td>
561 <td class="comment">MiniUPnP daemon source code</td>
562 <td><a href="miniupnpd-2.1.20191006.tar.gz.sig">Signature</a></td>
563</tr>
564<tr>
565 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20191005.tar.gz'>miniupnpd-2.1.20191005.tar.gz</a></td>
566 <td class="filesize">244100</td>
567 <td class="filedate">05/10/2019 21:33:08 +0000</td>
568 <td class="comment">MiniUPnP daemon source code</td>
569 <td><a href="miniupnpd-2.1.20191005.tar.gz.sig">Signature</a></td>
570</tr>
571<tr>
572 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20191003.tar.gz'>miniupnpd-2.1.20191003.tar.gz</a></td>
573 <td class="filesize">243287</td>
574 <td class="filedate">02/10/2019 22:23:51 +0000</td>
575 <td class="comment">MiniUPnP daemon source code</td>
576 <td><a href="miniupnpd-2.1.20191003.tar.gz.sig">Signature</a></td>
577</tr>
578<tr>
579 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190924.tar.gz'>miniupnpd-2.1.20190924.tar.gz</a></td>
580 <td class="filesize">241008</td>
581 <td class="filedate">24/09/2019 11:58:15 +0000</td>
582 <td class="comment">MiniUPnP daemon source code</td>
583 <td><a href="miniupnpd-2.1.20190924.tar.gz.sig">Signature</a></td>
584</tr>
585<tr>
586 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190902.tar.gz'>miniupnpd-2.1.20190902.tar.gz</a></td>
587 <td class="filesize">240742</td>
588 <td class="filedate">01/09/2019 23:03:03 +0000</td>
589 <td class="comment">MiniUPnP daemon source code</td>
590 <td><a href="miniupnpd-2.1.20190902.tar.gz.sig">Signature</a></td>
591</tr>
592<tr>
593 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190824.tar.gz'>miniupnpd-2.1.20190824.tar.gz</a></td>
594 <td class="filesize">240490</td>
595 <td class="filedate">24/08/2019 09:21:52 +0000</td>
596 <td class="comment">MiniUPnP daemon source code</td>
597 <td><a href="miniupnpd-2.1.20190824.tar.gz.sig">Signature</a></td>
598</tr>
599<tr>
600 <td class="filename"><a href='download.php?file=minissdpd-1.5.20190824.tar.gz'>minissdpd-1.5.20190824.tar.gz</a></td>
601 <td class="filesize">37300</td>
602 <td class="filedate">24/08/2019 09:17:32 +0000</td>
603 <td class="comment">MiniSSDPd source code</td>
604 <td><a href="minissdpd-1.5.20190824.tar.gz.sig">Signature</a></td>
605</tr>
606<tr>
607 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20190824.tar.gz'>miniupnpc-2.1.20190824.tar.gz</a></td>
608 <td class="filesize">94564</td>
609 <td class="filedate">24/08/2019 09:12:50 +0000</td>
610 <td class="comment">MiniUPnP client source code</td>
611 <td><a href="miniupnpc-2.1.20190824.tar.gz.sig">Signature</a></td>
612</tr>
613<tr>
614 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190630.tar.gz'>miniupnpd-2.1.20190630.tar.gz</a></td>
615 <td class="filesize">240466</td>
616 <td class="filedate">30/06/2019 20:27:38 +0000</td>
617 <td class="comment">MiniUPnP daemon source code</td>
618 <td><a href="miniupnpd-2.1.20190630.tar.gz.sig">Signature</a></td>
619</tr>
620<tr>
621 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190625.tar.gz'>miniupnpd-2.1.20190625.tar.gz</a></td>
622 <td class="filesize">240120</td>
623 <td class="filedate">25/06/2019 21:33:49 +0000</td>
624 <td class="comment">MiniUPnP daemon source code</td>
625 <td><a href="miniupnpd-2.1.20190625.tar.gz.sig">Signature</a></td>
626</tr>
627<tr>
628 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20190625.tar.gz'>miniupnpc-2.1.20190625.tar.gz</a></td>
629 <td class="filesize">94461</td>
630 <td class="filedate">25/06/2019 21:33:26 +0000</td>
631 <td class="comment">MiniUPnP client source code</td>
632 <td><a href="miniupnpc-2.1.20190625.tar.gz.sig">Signature</a></td>
633</tr>
634<tr>
635 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190502.tar.gz'>miniupnpd-2.1.20190502.tar.gz</a></td>
636 <td class="filesize">236052</td>
637 <td class="filedate">02/05/2019 17:22:23 +0000</td>
638 <td class="comment">MiniUPnP daemon source code</td>
639 <td><a href="miniupnpd-2.1.20190502.tar.gz.sig">Signature</a></td>
640</tr>
641<tr>
642 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20190408.tar.gz'>miniupnpc-2.1.20190408.tar.gz</a></td>
643 <td class="filesize">94216</td>
644 <td class="filedate">08/04/2019 12:50:21 +0000</td>
645 <td class="comment">MiniUPnP client source code</td>
646 <td><a href="miniupnpc-2.1.20190408.tar.gz.sig">Signature</a></td>
647</tr>
648<tr>
649 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190408.tar.gz'>miniupnpd-2.1.20190408.tar.gz</a></td>
650 <td class="filesize">235989</td>
651 <td class="filedate">08/04/2019 12:50:01 +0000</td>
652 <td class="comment">MiniUPnP daemon source code</td>
653 <td><a href="miniupnpd-2.1.20190408.tar.gz.sig">Signature</a></td>
654</tr>
655<tr>
656 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20190403.tar.gz'>miniupnpc-2.1.20190403.tar.gz</a></td>
657 <td class="filesize">94204</td>
658 <td class="filedate">03/04/2019 15:41:36 +0000</td>
659 <td class="comment">MiniUPnP client source code</td>
660 <td><a href="miniupnpc-2.1.20190403.tar.gz.sig">Signature</a></td>
661</tr>
662<tr>
663 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190403.tar.gz'>miniupnpd-2.1.20190403.tar.gz</a></td>
664 <td class="filesize">235909</td>
665 <td class="filedate">03/04/2019 15:41:17 +0000</td>
666 <td class="comment">MiniUPnP daemon source code</td>
667 <td><a href="miniupnpd-2.1.20190403.tar.gz.sig">Signature</a></td>
668</tr>
669<tr>
670 <td class="filename"><a href='download.php?file=minissdpd-1.5.20190210.tar.gz'>minissdpd-1.5.20190210.tar.gz</a></td>
671 <td class="filesize">37227</td>
672 <td class="filedate">10/02/2019 15:21:49 +0000</td>
673 <td class="comment">MiniSSDPd source code</td>
674 <td><a href="minissdpd-1.5.20190210.tar.gz.sig">Signature</a></td>
675</tr>
676<tr>
677 <td class="filename"><a href='download.php?file=miniupnpc-2.1.20190210.tar.gz'>miniupnpc-2.1.20190210.tar.gz</a></td>
678 <td class="filesize">94125</td>
679 <td class="filedate">10/02/2019 12:46:09 +0000</td>
680 <td class="comment">MiniUPnP client source code</td>
681 <td><a href="miniupnpc-2.1.20190210.tar.gz.sig">Signature</a></td>
682</tr>
683<tr>
684 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20190210.tar.gz'>miniupnpd-2.1.20190210.tar.gz</a></td>
685 <td class="filesize">235093</td>
686 <td class="filedate">10/02/2019 11:20:11 +0000</td>
687 <td class="comment">MiniUPnP daemon source code</td>
688 <td><a href="miniupnpd-2.1.20190210.tar.gz.sig">Signature</a></td>
689</tr>
690<tr>
691 <td class="filename"><a href='download.php?file=miniupnpd-2.1.20180706.tar.gz'>miniupnpd-2.1.20180706.tar.gz</a></td>
692 <td class="filesize">233675</td>
693 <td class="filedate">06/07/2018 12:44:24 +0000</td>
694 <td class="comment">MiniUPnP daemon source code</td>
695 <td><a href="miniupnpd-2.1.20180706.tar.gz.sig">Signature</a></td>
696</tr>
697<tr>
698 <td class="filename"><a href='download.php?file=miniupnpd-2.1.tar.gz'>miniupnpd-2.1.tar.gz</a></td>
699 <td class="filesize">225458</td>
700 <td class="filedate">08/05/2018 21:50:32 +0000</td>
701 <td class="comment">MiniUPnP daemon release source code</td>
702 <td><a href="miniupnpd-2.1.tar.gz.sig">Signature</a></td>
703</tr>
704<tr>
705 <td class="filename"><a href='download.php?file=miniupnpc-2.1.tar.gz'>miniupnpc-2.1.tar.gz</a></td>
706 <td class="filesize">91914</td>
707 <td class="filedate">07/05/2018 11:10:59 +0000</td>
708 <td class="comment">MiniUPnP client release source code</td>
709 <td><a href="miniupnpc-2.1.tar.gz.sig">Signature</a></td>
710</tr>
711<tr>
712 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20180503.tar.gz'>miniupnpd-2.0.20180503.tar.gz</a></td>
713 <td class="filesize">225454</td>
714 <td class="filedate">03/05/2018 08:33:10 +0000</td>
715 <td class="comment">MiniUPnP daemon source code</td>
716 <td></td>
717</tr>
718<tr>
719 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20180503.tar.gz'>miniupnpc-2.0.20180503.tar.gz</a></td>
720 <td class="filesize">88207</td>
721 <td class="filedate">03/05/2018 08:31:22 +0000</td>
722 <td class="comment">MiniUPnP client source code</td>
723 <td></td>
724</tr>
725<tr>
726 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20180422.tar.gz'>miniupnpd-2.0.20180422.tar.gz</a></td>
727 <td class="filesize">224942</td>
728 <td class="filedate">22/04/2018 19:48:54 +0000</td>
729 <td class="comment">MiniUPnP daemon source code</td>
730 <td></td>
731</tr>
732<tr>
733 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20180412.tar.gz'>miniupnpd-2.0.20180412.tar.gz</a></td>
734 <td class="filesize">224831</td>
735 <td class="filedate">12/04/2018 08:16:25 +0000</td>
736 <td class="comment">MiniUPnP daemon source code</td>
737 <td></td>
738</tr>
739<tr>
740 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20180410.tar.gz'>miniupnpd-2.0.20180410.tar.gz</a></td>
741 <td class="filesize">224736</td>
742 <td class="filedate">10/04/2018 07:58:28 +0000</td>
743 <td class="comment">MiniUPnP daemon source code</td>
744 <td></td>
745</tr>
746<tr>
747 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20180410.tar.gz'>miniupnpc-2.0.20180410.tar.gz</a></td>
748 <td class="filesize">87363</td>
749 <td class="filedate">10/04/2018 07:52:55 +0000</td>
750 <td class="comment">MiniUPnP client source code</td>
751 <td></td>
752</tr>
753<tr>
754 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20180406.tar.gz'>miniupnpc-2.0.20180406.tar.gz</a></td>
755 <td class="filesize">87374</td>
756 <td class="filedate">06/04/2018 10:55:21 +0000</td>
757 <td class="comment">MiniUPnP client source code</td>
758 <td></td>
759</tr>
760<tr>
761 <td class="filename"><a href='download.php?file=minissdpd-1.5.20180223.tar.gz'>minissdpd-1.5.20180223.tar.gz</a></td>
762 <td class="filesize">36179</td>
763 <td class="filedate">23/02/2018 14:24:07 +0000</td>
764 <td class="comment">MiniSSDPd source code</td>
765 <td></td>
766</tr>
767<tr>
768 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20180222.tar.gz'>miniupnpc-2.0.20180222.tar.gz</a></td>
769 <td class="filesize">87018</td>
770 <td class="filedate">22/02/2018 15:09:24 +0000</td>
771 <td class="comment">MiniUPnP client source code</td>
772 <td></td>
773</tr>
774<tr>
775 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20180222.tar.gz'>miniupnpd-2.0.20180222.tar.gz</a></td>
776 <td class="filesize">223697</td>
777 <td class="filedate">22/02/2018 15:09:14 +0000</td>
778 <td class="comment">MiniUPnP daemon source code</td>
779 <td></td>
780</tr>
781<tr>
782 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20180203.tar.gz'>miniupnpd-2.0.20180203.tar.gz</a></td>
783 <td class="filesize">223084</td>
784 <td class="filedate">03/02/2018 22:34:46 +0000</td>
785 <td class="comment">MiniUPnP daemon source code</td>
786 <td></td>
787</tr>
788<tr>
789 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20180203.tar.gz'>miniupnpc-2.0.20180203.tar.gz</a></td>
790 <td class="filesize">86772</td>
791 <td class="filedate">03/02/2018 22:34:32 +0000</td>
792 <td class="comment">MiniUPnP client source code</td>
793 <td></td>
794</tr>
795<tr>
796 <td class="filename"><a href='download.php?file=minissdpd-1.5.20180203.tar.gz'>minissdpd-1.5.20180203.tar.gz</a></td>
797 <td class="filesize">35848</td>
798 <td class="filedate">03/02/2018 22:33:08 +0000</td>
799 <td class="comment">MiniSSDPd source code</td>
800 <td></td>
801</tr>
802<tr>
803 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20171212.tar.gz'>miniupnpc-2.0.20171212.tar.gz</a></td>
804 <td class="filesize">86607</td>
805 <td class="filedate">12/12/2017 12:03:38 +0000</td>
806 <td class="comment">MiniUPnP client source code</td>
807 <td></td>
808</tr>
809<tr>
810 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20171212.tar.gz'>miniupnpd-2.0.20171212.tar.gz</a></td>
811 <td class="filesize">222617</td>
812 <td class="filedate">12/12/2017 12:03:32 +0000</td>
813 <td class="comment">MiniUPnP daemon source code</td>
814 <td></td>
815</tr>
816<tr>
817 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20171102.tar.gz'>miniupnpc-2.0.20171102.tar.gz</a></td>
818 <td class="filesize">86363</td>
819 <td class="filedate">02/11/2017 17:58:34 +0000</td>
820 <td class="comment">MiniUPnP client source code</td>
821 <td></td>
822</tr>
823<tr>
824 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20170509.tar.gz'>miniupnpc-2.0.20170509.tar.gz</a></td>
825 <td class="filesize">86055</td>
826 <td class="filedate">09/05/2017 10:14:56 +0000</td>
827 <td class="comment">MiniUPnP client source code</td>
828 <td></td>
829</tr>
830<tr>
831 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20170421.tar.gz'>miniupnpc-2.0.20170421.tar.gz</a></td>
832 <td class="filesize">85984</td>
833 <td class="filedate">21/04/2017 12:02:26 +0000</td>
834 <td class="comment">MiniUPnP client source code</td>
835 <td></td>
836</tr>
837<tr>
838 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20170421.tar.gz'>miniupnpd-2.0.20170421.tar.gz</a></td>
839 <td class="filesize">219191</td>
840 <td class="filedate">21/04/2017 12:02:06 +0000</td>
841 <td class="comment">MiniUPnP daemon source code</td>
842 <td></td>
843</tr>
844<tr>
845 <td class="filename"><a href='download.php?file=miniupnpd-2.0.20161216.tar.gz'>miniupnpd-2.0.20161216.tar.gz</a></td>
846 <td class="filesize">218119</td>
847 <td class="filedate">16/12/2016 09:34:08 +0000</td>
848 <td class="comment">MiniUPnP daemon source code</td>
849 <td></td>
850</tr>
851<tr>
852 <td class="filename"><a href='download.php?file=miniupnpc-2.0.20161216.tar.gz'>miniupnpc-2.0.20161216.tar.gz</a></td>
853 <td class="filesize">85780</td>
854 <td class="filedate">16/12/2016 09:34:03 +0000</td>
855 <td class="comment">MiniUPnP client source code</td>
856 <td></td>
857</tr>
858<tr>
859 <td class="filename"><a href='download.php?file=minissdpd-1.5.20161216.tar.gz'>minissdpd-1.5.20161216.tar.gz</a></td>
860 <td class="filesize">35078</td>
861 <td class="filedate">16/12/2016 09:33:59 +0000</td>
862 <td class="comment">MiniSSDPd source code</td>
863 <td></td>
864</tr>
865<tr>
866 <td class="filename"><a href='download.php?file=miniupnpd-2.0.tar.gz'>miniupnpd-2.0.tar.gz</a></td>
867 <td class="filesize">217802</td>
868 <td class="filedate">19/04/2016 21:12:01 +0000</td>
869 <td class="comment">MiniUPnP daemon release source code</td>
870 <td><a href="miniupnpd-2.0.tar.gz.sig">Signature</a></td>
871</tr>
872<tr>
873 <td class="filename"><a href='download.php?file=miniupnpc-2.0.tar.gz'>miniupnpc-2.0.tar.gz</a></td>
874 <td class="filesize">85287</td>
875 <td class="filedate">19/04/2016 21:07:52 +0000</td>
876 <td class="comment">MiniUPnP client release source code</td>
877 <td></td>
878</tr>
879<tr>
880 <td class="filename"><a href='download.php?file=minissdpd-1.5.20160301.tar.gz'>minissdpd-1.5.20160301.tar.gz</a></td>
881 <td class="filesize">34827</td>
882 <td class="filedate">01/03/2016 18:08:23 +0000</td>
883 <td class="comment">MiniSSDPd source code</td>
884 <td></td>
885</tr>
886<tr>
887 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20160222.tar.gz'>miniupnpd-1.9.20160222.tar.gz</a></td>
888 <td class="filesize">217541</td>
889 <td class="filedate">22/02/2016 10:21:40 +0000</td>
890 <td class="comment">MiniUPnP daemon source code</td>
891 <td></td>
892</tr>
893<tr>
894 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20160216.tar.gz'>miniupnpd-1.9.20160216.tar.gz</a></td>
895 <td class="filesize">217007</td>
896 <td class="filedate">16/02/2016 12:41:44 +0000</td>
897 <td class="comment">MiniUPnP daemon source code</td>
898 <td></td>
899</tr>
900<tr>
901 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20160212.tar.gz'>miniupnpd-1.9.20160212.tar.gz</a></td>
902 <td class="filesize">215866</td>
903 <td class="filedate">12/02/2016 15:22:04 +0000</td>
904 <td class="comment">MiniUPnP daemon source code</td>
905 <td></td>
906</tr>
907<tr>
908 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20160209.tar.gz'>miniupnpd-1.9.20160209.tar.gz</a></td>
909 <td class="filesize">213416</td>
910 <td class="filedate">09/02/2016 09:47:03 +0000</td>
911 <td class="comment">MiniUPnP daemon source code</td>
912 <td></td>
913</tr>
914<tr>
915 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20160209.tar.gz'>miniupnpc-1.9.20160209.tar.gz</a></td>
916 <td class="filesize">85268</td>
917 <td class="filedate">09/02/2016 09:44:50 +0000</td>
918 <td class="comment">MiniUPnP client source code</td>
919 <td></td>
920</tr>
921<tr>
922 <td class="filename"><a href='download.php?file=minissdpd-1.5.20160119.tar.gz'>minissdpd-1.5.20160119.tar.gz</a></td>
923 <td class="filesize">34711</td>
924 <td class="filedate">19/01/2016 13:39:51 +0000</td>
925 <td class="comment">MiniSSDPd source code</td>
926 <td></td>
927</tr>
928<tr>
929 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20160113.tar.gz'>miniupnpd-1.9.20160113.tar.gz</a></td>
930 <td class="filesize">211437</td>
931 <td class="filedate">13/01/2016 16:03:14 +0000</td>
932 <td class="comment">MiniUPnP daemon source code</td>
933 <td></td>
934</tr>
935<tr>
936 <td class="filename"><a href='download.php?file=minissdpd-1.5.tar.gz'>minissdpd-1.5.tar.gz</a></td>
937 <td class="filesize">34404</td>
938 <td class="filedate">13/01/2016 15:26:53 +0000</td>
939 <td class="comment">MiniSSDPd release source code</td>
940 <td></td>
941</tr>
942<tr>
943 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20151212.tar.gz'>miniupnpd-1.9.20151212.tar.gz</a></td>
944 <td class="filesize">210912</td>
945 <td class="filedate">12/12/2015 10:06:07 +0000</td>
946 <td class="comment">MiniUPnP daemon source code</td>
947 <td></td>
948</tr>
949<tr>
950 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20151118.tar.gz'>miniupnpd-1.9.20151118.tar.gz</a></td>
951 <td class="filesize">210322</td>
952 <td class="filedate">18/11/2015 08:59:46 +0000</td>
953 <td class="comment">MiniUPnP daemon source code</td>
954 <td></td>
955</tr>
956<tr>
957 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20151026.tar.gz'>miniupnpc-1.9.20151026.tar.gz</a></td>
958 <td class="filesize">84208</td>
959 <td class="filedate">26/10/2015 17:07:34 +0000</td>
960 <td class="comment">MiniUPnP client source code</td>
961 <td></td>
962</tr>
963<tr>
964 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20151008.tar.gz'>miniupnpc-1.9.20151008.tar.gz</a></td>
965 <td class="filesize">83538</td>
966 <td class="filedate">08/10/2015 16:22:28 +0000</td>
967 <td class="comment">MiniUPnP client source code</td>
968 <td></td>
969</tr>
970<tr>
971 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20150922.tar.gz'>miniupnpd-1.9.20150922.tar.gz</a></td>
972 <td class="filesize">208700</td>
973 <td class="filedate">22/09/2015 10:21:50 +0000</td>
974 <td class="comment">MiniUPnP daemon source code</td>
975 <td></td>
976</tr>
977<tr>
978 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20150918.zip'>upnpc-exe-win32-20150918.zip</a></td>
979 <td class="filesize">100004</td>
980 <td class="filedate">18/09/2015 12:50:51 +0000</td>
981 <td class="comment">Windows executable</td>
982 <td></td>
983</tr>
984<tr>
985 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150917.tar.gz'>miniupnpc-1.9.20150917.tar.gz</a></td>
986 <td class="filesize">82609</td>
987 <td class="filedate">17/09/2015 14:09:14 +0000</td>
988 <td class="comment">MiniUPnP client source code</td>
989 <td></td>
990</tr>
991<tr>
992 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20150824.zip'>upnpc-exe-win32-20150824.zip</a></td>
993 <td class="filesize">99520</td>
994 <td class="filedate">24/08/2015 15:25:18 +0000</td>
995 <td class="comment">Windows executable</td>
996 <td></td>
997</tr>
998<tr>
999 <td class="filename"><a href='download.php?file=minissdpd-1.4.tar.gz'>minissdpd-1.4.tar.gz</a></td>
1000 <td class="filesize">32017</td>
1001 <td class="filedate">06/08/2015 13:38:37 +0000</td>
1002 <td class="comment">MiniSSDPd release source code</td>
1003 <td></td>
1004</tr>
1005<tr>
1006 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150730.tar.gz'>miniupnpc-1.9.20150730.tar.gz</a></td>
1007 <td class="filesize">81431</td>
1008 <td class="filedate">29/07/2015 22:10:00 +0000</td>
1009 <td class="comment">MiniUPnP client source code</td>
1010 <td></td>
1011</tr>
1012<tr>
1013 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20150721.tar.gz'>miniupnpd-1.9.20150721.tar.gz</a></td>
1014 <td class="filesize">207562</td>
1015 <td class="filedate">21/07/2015 13:35:51 +0000</td>
1016 <td class="comment">MiniUPnP daemon source code</td>
1017 <td></td>
1018</tr>
1019<tr>
1020 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150721.tar.gz'>miniupnpc-1.9.20150721.tar.gz</a></td>
1021 <td class="filesize">80521</td>
1022 <td class="filedate">21/07/2015 13:27:00 +0000</td>
1023 <td class="comment">MiniUPnP client source code</td>
1024 <td></td>
1025</tr>
1026<tr>
1027 <td class="filename"><a href='download.php?file=libnatpmp-20150609.tar.gz'>libnatpmp-20150609.tar.gz</a></td>
1028 <td class="filesize">24392</td>
1029 <td class="filedate">09/06/2015 15:40:28 +0000</td>
1030 <td class="comment">libnatpmp source code</td>
1031 <td></td>
1032</tr>
1033<tr>
1034 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150609.tar.gz'>miniupnpc-1.9.20150609.tar.gz</a></td>
1035 <td class="filesize">79311</td>
1036 <td class="filedate">09/06/2015 15:39:48 +0000</td>
1037 <td class="comment">MiniUPnP client source code</td>
1038 <td></td>
1039</tr>
1040<tr>
1041 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20150609.tar.gz'>miniupnpd-1.9.20150609.tar.gz</a></td>
1042 <td class="filesize">207088</td>
1043 <td class="filedate">09/06/2015 15:39:36 +0000</td>
1044 <td class="comment">MiniUPnP daemon source code</td>
1045 <td></td>
1046</tr>
1047<tr>
1048 <td class="filename"><a href='download.php?file=minissdpd-1.3.20150527.tar.gz'>minissdpd-1.3.20150527.tar.gz</a></td>
1049 <td class="filesize">31025</td>
1050 <td class="filedate">27/05/2015 09:17:15 +0000</td>
1051 <td class="comment">MiniSSDPd source code</td>
1052 <td></td>
1053</tr>
1054<tr>
1055 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150522.tar.gz'>miniupnpc-1.9.20150522.tar.gz</a></td>
1056 <td class="filesize">79080</td>
1057 <td class="filedate">22/05/2015 11:02:27 +0000</td>
1058 <td class="comment">MiniUPnP client source code</td>
1059 <td></td>
1060</tr>
1061<tr>
1062 <td class="filename"><a href='download.php?file=minissdpd-1.3.20150522.tar.gz'>minissdpd-1.3.20150522.tar.gz</a></td>
1063 <td class="filesize">30334</td>
1064 <td class="filedate">22/05/2015 11:02:04 +0000</td>
1065 <td class="comment">MiniSSDPd source code</td>
1066 <td></td>
1067</tr>
1068<tr>
1069 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20150430.tar.gz'>miniupnpd-1.9.20150430.tar.gz</a></td>
1070 <td class="filesize">205930</td>
1071 <td class="filedate">30/04/2015 09:09:27 +0000</td>
1072 <td class="comment">MiniUPnP daemon source code</td>
1073 <td></td>
1074</tr>
1075<tr>
1076 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150430.tar.gz'>miniupnpc-1.9.20150430.tar.gz</a></td>
1077 <td class="filesize">78459</td>
1078 <td class="filedate">30/04/2015 08:39:31 +0000</td>
1079 <td class="comment">MiniUPnP client source code</td>
1080 <td></td>
1081</tr>
1082<tr>
1083 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150427.tar.gz'>miniupnpc-1.9.20150427.tar.gz</a></td>
1084 <td class="filesize">78424</td>
1085 <td class="filedate">27/04/2015 16:08:42 +0000</td>
1086 <td class="comment">MiniUPnP client source code</td>
1087 <td></td>
1088</tr>
1089<tr>
1090 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20150427.tar.gz'>miniupnpd-1.9.20150427.tar.gz</a></td>
1091 <td class="filesize">191157</td>
1092 <td class="filedate">27/04/2015 16:08:27 +0000</td>
1093 <td class="comment">MiniUPnP daemon source code</td>
1094 <td></td>
1095</tr>
1096<tr>
1097 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20150307.tar.gz'>miniupnpd-1.9.20150307.tar.gz</a></td>
1098 <td class="filesize">190913</td>
1099 <td class="filedate">07/03/2015 16:11:51 +0000</td>
1100 <td class="comment">MiniUPnP daemon source code</td>
1101 <td></td>
1102</tr>
1103<tr>
1104 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20150206.tar.gz'>miniupnpc-1.9.20150206.tar.gz</a></td>
1105 <td class="filesize">76864</td>
1106 <td class="filedate">06/02/2015 14:38:00 +0000</td>
1107 <td class="comment">MiniUPnP client source code</td>
1108 <td></td>
1109</tr>
1110<tr>
1111 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20141209.tar.gz'>miniupnpd-1.9.20141209.tar.gz</a></td>
1112 <td class="filesize">193183</td>
1113 <td class="filedate">09/12/2014 09:58:34 +0000</td>
1114 <td class="comment">MiniUPnP daemon source code</td>
1115 <td></td>
1116</tr>
1117<tr>
1118 <td class="filename"><a href='download.php?file=minissdpd-1.3.tar.gz'>minissdpd-1.3.tar.gz</a></td>
1119 <td class="filesize">30326</td>
1120 <td class="filedate">09/12/2014 09:57:30 +0000</td>
1121 <td class="comment">MiniSSDPd release source code</td>
1122 <td></td>
1123</tr>
1124<tr>
1125 <td class="filename"><a href='download.php?file=minissdpd-1.2.20141204.tar.gz'>minissdpd-1.2.20141204.tar.gz</a></td>
1126 <td class="filesize">26978</td>
1127 <td class="filedate">04/12/2014 10:55:26 +0000</td>
1128 <td class="comment">MiniSSDPd source code</td>
1129 <td></td>
1130</tr>
1131<tr>
1132 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20141204.tar.gz'>miniupnpd-1.9.20141204.tar.gz</a></td>
1133 <td class="filesize">192597</td>
1134 <td class="filedate">04/12/2014 10:55:03 +0000</td>
1135 <td class="comment">MiniUPnP daemon source code</td>
1136 <td></td>
1137</tr>
1138<tr>
1139 <td class="filename"><a href='download.php?file=minissdpd-1.2.20141128.tar.gz'>minissdpd-1.2.20141128.tar.gz</a></td>
1140 <td class="filesize">26795</td>
1141 <td class="filedate">28/11/2014 16:33:10 +0000</td>
1142 <td class="comment">MiniSSDPd source code</td>
1143 <td></td>
1144</tr>
1145<tr>
1146 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20141128.tar.gz'>miniupnpd-1.9.20141128.tar.gz</a></td>
1147 <td class="filesize">192558</td>
1148 <td class="filedate">28/11/2014 13:31:36 +0000</td>
1149 <td class="comment">MiniUPnP daemon source code</td>
1150 <td></td>
1151</tr>
1152<tr>
1153 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20141128.tar.gz'>miniupnpc-1.9.20141128.tar.gz</a></td>
1154 <td class="filesize">76541</td>
1155 <td class="filedate">28/11/2014 13:31:15 +0000</td>
1156 <td class="comment">MiniUPnP client source code</td>
1157 <td></td>
1158</tr>
1159<tr>
1160 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20141117.tar.gz'>miniupnpc-1.9.20141117.tar.gz</a></td>
1161 <td class="filesize">73865</td>
1162 <td class="filedate">17/11/2014 09:51:36 +0000</td>
1163 <td class="comment">MiniUPnP client source code</td>
1164 <td></td>
1165</tr>
1166<tr>
1167 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20141113.tar.gz'>miniupnpc-1.9.20141113.tar.gz</a></td>
1168 <td class="filesize">72857</td>
1169 <td class="filedate">13/11/2014 10:36:44 +0000</td>
1170 <td class="comment">MiniUPnP client source code</td>
1171 <td></td>
1172</tr>
1173<tr>
1174 <td class="filename"><a href='download.php?file=minissdpd-1.2.20141108.tar.gz'>minissdpd-1.2.20141108.tar.gz</a></td>
1175 <td class="filesize">22001</td>
1176 <td class="filedate">08/11/2014 13:55:41 +0000</td>
1177 <td class="comment">MiniSSDPd source code</td>
1178 <td></td>
1179</tr>
1180<tr>
1181 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20141108.tar.gz'>miniupnpc-1.9.20141108.tar.gz</a></td>
1182 <td class="filesize">72781</td>
1183 <td class="filedate">08/11/2014 13:53:48 +0000</td>
1184 <td class="comment">MiniUPnP client source code</td>
1185 <td></td>
1186</tr>
1187<tr>
1188 <td class="filename"><a href='download.php?file=miniupnpd-1.9.20141108.tar.gz'>miniupnpd-1.9.20141108.tar.gz</a></td>
1189 <td class="filesize">192413</td>
1190 <td class="filedate">08/11/2014 13:53:38 +0000</td>
1191 <td class="comment">MiniUPnP daemon source code</td>
1192 <td></td>
1193</tr>
1194<tr>
1195 <td class="filename"><a href='download.php?file=miniupnpd-1.9.tar.gz'>miniupnpd-1.9.tar.gz</a></td>
1196 <td class="filesize">192183</td>
1197 <td class="filedate">27/10/2014 16:45:34 +0000</td>
1198 <td class="comment">MiniUPnP daemon release source code</td>
1199 <td></td>
1200</tr>
1201<tr>
1202 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20141027.tar.gz'>miniupnpc-1.9.20141027.tar.gz</a></td>
1203 <td class="filesize">76763</td>
1204 <td class="filedate">27/10/2014 16:45:25 +0000</td>
1205 <td class="comment">MiniUPnP client source code</td>
1206 <td></td>
1207</tr>
1208<tr>
1209 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20141022.tar.gz'>miniupnpd-1.8.20141022.tar.gz</a></td>
1210 <td class="filesize">191630</td>
1211 <td class="filedate">22/10/2014 09:17:41 +0000</td>
1212 <td class="comment">MiniUPnP daemon source code</td>
1213 <td></td>
1214</tr>
1215<tr>
1216 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20141021.tar.gz'>miniupnpd-1.8.20141021.tar.gz</a></td>
1217 <td class="filesize">191270</td>
1218 <td class="filedate">21/10/2014 14:18:58 +0000</td>
1219 <td class="comment">MiniUPnP daemon source code</td>
1220 <td></td>
1221</tr>
1222<tr>
1223 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20140911.tar.gz'>miniupnpc-1.9.20140911.tar.gz</a></td>
1224 <td class="filesize">76855</td>
1225 <td class="filedate">11/09/2014 14:15:23 +0000</td>
1226 <td class="comment">MiniUPnP client source code</td>
1227 <td></td>
1228</tr>
1229<tr>
1230 <td class="filename"><a href='download.php?file=minissdpd-1.2.20140906.tar.gz'>minissdpd-1.2.20140906.tar.gz</a></td>
1231 <td class="filesize">21956</td>
1232 <td class="filedate">06/09/2014 08:34:10 +0000</td>
1233 <td class="comment">MiniSSDPd source code</td>
1234 <td></td>
1235</tr>
1236<tr>
1237 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140906.tar.gz'>miniupnpd-1.8.20140906.tar.gz</a></td>
1238 <td class="filesize">191183</td>
1239 <td class="filedate">06/09/2014 08:34:02 +0000</td>
1240 <td class="comment">MiniUPnP daemon source code</td>
1241 <td></td>
1242</tr>
1243<tr>
1244 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20140906.tar.gz'>miniupnpc-1.9.20140906.tar.gz</a></td>
1245 <td class="filesize">76791</td>
1246 <td class="filedate">06/09/2014 08:33:45 +0000</td>
1247 <td class="comment">MiniUPnP client source code</td>
1248 <td></td>
1249</tr>
1250<tr>
1251 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20140701.tar.gz'>miniupnpc-1.9.20140701.tar.gz</a></td>
1252 <td class="filesize">76735</td>
1253 <td class="filedate">01/07/2014 13:06:51 +0000</td>
1254 <td class="comment">MiniUPnP client source code</td>
1255 <td></td>
1256</tr>
1257<tr>
1258 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20140610.tar.gz'>miniupnpc-1.9.20140610.tar.gz</a></td>
1259 <td class="filesize">76674</td>
1260 <td class="filedate">10/06/2014 10:28:27 +0000</td>
1261 <td class="comment">MiniUPnP client source code</td>
1262 <td></td>
1263</tr>
1264<tr>
1265 <td class="filename"><a href='download.php?file=minissdpd-1.2.20140610.tar.gz'>minissdpd-1.2.20140610.tar.gz</a></td>
1266 <td class="filesize">21909</td>
1267 <td class="filedate">10/06/2014 10:03:29 +0000</td>
1268 <td class="comment">MiniSSDPd source code</td>
1269 <td></td>
1270</tr>
1271<tr>
1272 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140523.tar.gz'>miniupnpd-1.8.20140523.tar.gz</a></td>
1273 <td class="filesize">190936</td>
1274 <td class="filedate">23/05/2014 15:48:03 +0000</td>
1275 <td class="comment">MiniUPnP daemon source code</td>
1276 <td></td>
1277</tr>
1278<tr>
1279 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20140422.zip'>upnpc-exe-win32-20140422.zip</a></td>
1280 <td class="filesize">97505</td>
1281 <td class="filedate">22/04/2014 10:10:07 +0000</td>
1282 <td class="comment">Windows executable</td>
1283 <td></td>
1284</tr>
1285<tr>
1286 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140422.tar.gz'>miniupnpd-1.8.20140422.tar.gz</a></td>
1287 <td class="filesize">187225</td>
1288 <td class="filedate">22/04/2014 08:58:56 +0000</td>
1289 <td class="comment">MiniUPnP daemon source code</td>
1290 <td></td>
1291</tr>
1292<tr>
1293 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140401.tar.gz'>miniupnpd-1.8.20140401.tar.gz</a></td>
1294 <td class="filesize">183131</td>
1295 <td class="filedate">01/04/2014 10:07:20 +0000</td>
1296 <td class="comment">MiniUPnP daemon source code</td>
1297 <td></td>
1298</tr>
1299<tr>
1300 <td class="filename"><a href='download.php?file=miniupnpc-1.9.20140401.tar.gz'>miniupnpc-1.9.20140401.tar.gz</a></td>
1301 <td class="filesize">74703</td>
1302 <td class="filedate">01/04/2014 09:49:46 +0000</td>
1303 <td class="comment">MiniUPnP client source code</td>
1304 <td></td>
1305</tr>
1306<tr>
1307 <td class="filename"><a href='download.php?file=libnatpmp-20140401.tar.gz'>libnatpmp-20140401.tar.gz</a></td>
1308 <td class="filesize">23302</td>
1309 <td class="filedate">01/04/2014 09:49:44 +0000</td>
1310 <td class="comment">libnatpmp source code</td>
1311 <td></td>
1312</tr>
1313<tr>
1314 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140313.tar.gz'>miniupnpd-1.8.20140313.tar.gz</a></td>
1315 <td class="filesize">177120</td>
1316 <td class="filedate">13/03/2014 10:39:11 +0000</td>
1317 <td class="comment">MiniUPnP daemon source code</td>
1318 <td></td>
1319</tr>
1320<tr>
1321 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140310.tar.gz'>miniupnpd-1.8.20140310.tar.gz</a></td>
1322 <td class="filesize">176585</td>
1323 <td class="filedate">09/03/2014 23:16:49 +0000</td>
1324 <td class="comment">MiniUPnP daemon source code</td>
1325 <td></td>
1326</tr>
1327<tr>
1328 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140225.tar.gz'>miniupnpd-1.8.20140225.tar.gz</a></td>
1329 <td class="filesize">175183</td>
1330 <td class="filedate">25/02/2014 11:01:29 +0000</td>
1331 <td class="comment">MiniUPnP daemon source code</td>
1332 <td></td>
1333</tr>
1334<tr>
1335 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140203.tar.gz'>miniupnpd-1.8.20140203.tar.gz</a></td>
1336 <td class="filesize">170112</td>
1337 <td class="filedate">03/02/2014 09:56:05 +0000</td>
1338 <td class="comment">MiniUPnP daemon source code</td>
1339 <td></td>
1340</tr>
1341<tr>
1342 <td class="filename"><a href='download.php?file=miniupnpc-1.9.tar.gz'>miniupnpc-1.9.tar.gz</a></td>
1343 <td class="filesize">74230</td>
1344 <td class="filedate">31/01/2014 13:57:40 +0000</td>
1345 <td class="comment">MiniUPnP client release source code</td>
1346 <td></td>
1347</tr>
1348<tr>
1349 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20140127.tar.gz'>miniupnpd-1.8.20140127.tar.gz</a></td>
1350 <td class="filesize">170467</td>
1351 <td class="filedate">27/01/2014 11:25:34 +0000</td>
1352 <td class="comment">MiniUPnP daemon source code</td>
1353 <td></td>
1354</tr>
1355<tr>
1356 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20140117.zip'>upnpc-exe-win32-20140117.zip</a></td>
1357 <td class="filesize">97270</td>
1358 <td class="filedate">17/01/2014 11:37:53 +0000</td>
1359 <td class="comment">Windows executable</td>
1360 <td></td>
1361</tr>
1362<tr>
1363 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20131216.tar.gz'>miniupnpd-1.8.20131216.tar.gz</a></td>
1364 <td class="filesize">170277</td>
1365 <td class="filedate">16/12/2013 16:15:40 +0000</td>
1366 <td class="comment">MiniUPnP daemon source code</td>
1367 <td></td>
1368</tr>
1369<tr>
1370 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20131213.tar.gz'>miniupnpd-1.8.20131213.tar.gz</a></td>
1371 <td class="filesize">169753</td>
1372 <td class="filedate">13/12/2013 16:18:10 +0000</td>
1373 <td class="comment">MiniUPnP daemon source code</td>
1374 <td></td>
1375</tr>
1376<tr>
1377 <td class="filename"><a href='download.php?file=miniupnpc-1.8.20131209.tar.gz'>miniupnpc-1.8.20131209.tar.gz</a></td>
1378 <td class="filesize">73900</td>
1379 <td class="filedate">09/12/2013 20:52:54 +0000</td>
1380 <td class="comment">MiniUPnP client source code</td>
1381 <td></td>
1382</tr>
1383<tr>
1384 <td class="filename"><a href='download.php?file=libnatpmp-20131126.tar.gz'>libnatpmp-20131126.tar.gz</a></td>
1385 <td class="filesize">22972</td>
1386 <td class="filedate">26/11/2013 08:51:36 +0000</td>
1387 <td class="comment">libnatpmp source code</td>
1388 <td></td>
1389</tr>
1390<tr>
1391 <td class="filename"><a href='download.php?file=miniupnpc-1.8.20131007.tar.gz'>miniupnpc-1.8.20131007.tar.gz</a></td>
1392 <td class="filesize">73750</td>
1393 <td class="filedate">07/10/2013 10:10:25 +0000</td>
1394 <td class="comment">MiniUPnP client source code</td>
1395 <td></td>
1396</tr>
1397<tr>
1398 <td class="filename"><a href='download.php?file=libnatpmp-20130911.tar.gz'>libnatpmp-20130911.tar.gz</a></td>
1399 <td class="filesize">18744</td>
1400 <td class="filedate">11/09/2013 07:35:51 +0000</td>
1401 <td class="comment">libnatpmp source code</td>
1402 <td></td>
1403</tr>
1404<tr>
1405 <td class="filename"><a href='download.php?file=libnatpmp-20130910.tar.gz'>libnatpmp-20130910.tar.gz</a></td>
1406 <td class="filesize">18734</td>
1407 <td class="filedate">10/09/2013 20:15:34 +0000</td>
1408 <td class="comment">libnatpmp source code</td>
1409 <td></td>
1410</tr>
1411<tr>
1412 <td class="filename"><a href='download.php?file=minissdpd-1.2.20130907.tar.gz'>minissdpd-1.2.20130907.tar.gz</a></td>
1413 <td class="filesize">20237</td>
1414 <td class="filedate">07/09/2013 06:46:31 +0000</td>
1415 <td class="comment">MiniSSDPd source code</td>
1416 <td></td>
1417</tr>
1418<tr>
1419 <td class="filename"><a href='download.php?file=minissdpd-1.2.20130819.tar.gz'>minissdpd-1.2.20130819.tar.gz</a></td>
1420 <td class="filesize">20772</td>
1421 <td class="filedate">19/08/2013 16:50:29 +0000</td>
1422 <td class="comment">MiniSSDPd source code</td>
1423 <td></td>
1424</tr>
1425<tr>
1426 <td class="filename"><a href='download.php?file=miniupnpc-1.8.20130801.tar.gz'>miniupnpc-1.8.20130801.tar.gz</a></td>
1427 <td class="filesize">73426</td>
1428 <td class="filedate">01/08/2013 21:38:05 +0000</td>
1429 <td class="comment">MiniUPnP client source code</td>
1430 <td></td>
1431</tr>
1432<tr>
1433 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20130730.tar.gz'>miniupnpd-1.8.20130730.tar.gz</a></td>
1434 <td class="filesize">149904</td>
1435 <td class="filedate">30/07/2013 11:37:48 +0000</td>
1436 <td class="comment">MiniUPnP daemon source code</td>
1437 <td></td>
1438</tr>
1439<tr>
1440 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20130607.tar.gz'>miniupnpd-1.8.20130607.tar.gz</a></td>
1441 <td class="filesize">149521</td>
1442 <td class="filedate">07/06/2013 08:46:17 +0000</td>
1443 <td class="comment">MiniUPnP daemon source code</td>
1444 <td></td>
1445</tr>
1446<tr>
1447 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20130521.tar.gz'>miniupnpd-1.8.20130521.tar.gz</a></td>
1448 <td class="filesize">149276</td>
1449 <td class="filedate">21/05/2013 09:01:33 +0000</td>
1450 <td class="comment">MiniUPnP daemon source code</td>
1451 <td></td>
1452</tr>
1453<tr>
1454 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20130503.tar.gz'>miniupnpd-1.8.20130503.tar.gz</a></td>
1455 <td class="filesize">148420</td>
1456 <td class="filedate">03/05/2013 19:27:16 +0000</td>
1457 <td class="comment">MiniUPnP daemon source code</td>
1458 <td></td>
1459</tr>
1460<tr>
1461 <td class="filename"><a href='download.php?file=miniupnpc-1.8.20130503.tar.gz'>miniupnpc-1.8.20130503.tar.gz</a></td>
1462 <td class="filesize">71858</td>
1463 <td class="filedate">03/05/2013 19:27:07 +0000</td>
1464 <td class="comment">MiniUPnP client source code</td>
1465 <td></td>
1466</tr>
1467<tr>
1468 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20130426.tar.gz'>miniupnpd-1.8.20130426.tar.gz</a></td>
1469 <td class="filesize">147890</td>
1470 <td class="filedate">26/04/2013 16:57:20 +0000</td>
1471 <td class="comment">MiniUPnP daemon source code</td>
1472 <td></td>
1473</tr>
1474<tr>
1475 <td class="filename"><a href='download.php?file=miniupnpc-1.8.20130211.tar.gz'>miniupnpc-1.8.20130211.tar.gz</a></td>
1476 <td class="filesize">70723</td>
1477 <td class="filedate">11/02/2013 10:32:44 +0000</td>
1478 <td class="comment">MiniUPnP client source code</td>
1479 <td></td>
1480</tr>
1481<tr>
1482 <td class="filename"><a href='download.php?file=miniupnpd-1.8.20130207.tar.gz'>miniupnpd-1.8.20130207.tar.gz</a></td>
1483 <td class="filesize">147325</td>
1484 <td class="filedate">07/02/2013 12:29:32 +0000</td>
1485 <td class="comment">MiniUPnP daemon source code</td>
1486 <td></td>
1487</tr>
1488<tr>
1489 <td class="filename"><a href='download.php?file=miniupnpc-1.8.tar.gz'>miniupnpc-1.8.tar.gz</a></td>
1490 <td class="filesize">70624</td>
1491 <td class="filedate">06/02/2013 14:31:06 +0000</td>
1492 <td class="comment">MiniUPnP client release source code</td>
1493 <td></td>
1494</tr>
1495<tr>
1496 <td class="filename"><a href='download.php?file=miniupnpd-1.8.tar.gz'>miniupnpd-1.8.tar.gz</a></td>
1497 <td class="filesize">146679</td>
1498 <td class="filedate">06/02/2013 14:30:59 +0000</td>
1499 <td class="comment">MiniUPnP daemon release source code</td>
1500 <td></td>
1501</tr>
1502<tr>
1503 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20121009.zip'>upnpc-exe-win32-20121009.zip</a></td>
1504 <td class="filesize">96513</td>
1505 <td class="filedate">09/10/2012 17:54:12 +0000</td>
1506 <td class="comment">Windows executable</td>
1507 <td></td>
1508</tr>
1509<tr>
1510 <td class="filename"><a href='download.php?file=miniupnpd-1.7.20121005.tar.gz'>miniupnpd-1.7.20121005.tar.gz</a></td>
1511 <td class="filesize">144393</td>
1512 <td class="filedate">04/10/2012 22:39:05 +0000</td>
1513 <td class="comment">MiniUPnP daemon source code</td>
1514 <td></td>
1515</tr>
1516<tr>
1517 <td class="filename"><a href='download.php?file=miniupnpc-1.7.20120830.tar.gz'>miniupnpc-1.7.20120830.tar.gz</a></td>
1518 <td class="filesize">70074</td>
1519 <td class="filedate">30/08/2012 08:41:51 +0000</td>
1520 <td class="comment">MiniUPnP client source code</td>
1521 <td></td>
1522</tr>
1523<tr>
1524 <td class="filename"><a href='download.php?file=miniupnpd-1.7.20120824.tar.gz'>miniupnpd-1.7.20120824.tar.gz</a></td>
1525 <td class="filesize">141960</td>
1526 <td class="filedate">24/08/2012 18:15:01 +0000</td>
1527 <td class="comment">MiniUPnP daemon source code</td>
1528 <td></td>
1529</tr>
1530<tr>
1531 <td class="filename"><a href='download.php?file=libnatpmp-20120821.tar.gz'>libnatpmp-20120821.tar.gz</a></td>
1532 <td class="filesize">17832</td>
1533 <td class="filedate">21/08/2012 17:24:46 +0000</td>
1534 <td class="comment">libnatpmp source code</td>
1535 <td></td>
1536</tr>
1537<tr>
1538 <td class="filename"><a href='download.php?file=miniupnpc-1.7.20120714.tar.gz'>miniupnpc-1.7.20120714.tar.gz</a></td>
1539 <td class="filesize">69570</td>
1540 <td class="filedate">14/07/2012 14:40:47 +0000</td>
1541 <td class="comment">MiniUPnP client source code</td>
1542 <td></td>
1543</tr>
1544<tr>
1545 <td class="filename"><a href='download.php?file=miniupnpc-1.7.20120711.tar.gz'>miniupnpc-1.7.20120711.tar.gz</a></td>
1546 <td class="filesize">69580</td>
1547 <td class="filedate">10/07/2012 22:27:05 +0000</td>
1548 <td class="comment">MiniUPnP client source code</td>
1549 <td></td>
1550</tr>
1551<tr>
1552 <td class="filename"><a href='download.php?file=miniupnpd-1.7.20120711.tar.gz'>miniupnpd-1.7.20120711.tar.gz</a></td>
1553 <td class="filesize">141380</td>
1554 <td class="filedate">10/07/2012 22:26:58 +0000</td>
1555 <td class="comment">MiniUPnP daemon source code</td>
1556 <td></td>
1557</tr>
1558<tr>
1559 <td class="filename"><a href='download.php?file=miniupnpd-1.7.tar.gz'>miniupnpd-1.7.tar.gz</a></td>
1560 <td class="filesize">138047</td>
1561 <td class="filedate">27/05/2012 23:13:30 +0000</td>
1562 <td class="comment">MiniUPnP daemon release source code</td>
1563 <td></td>
1564</tr>
1565<tr>
1566 <td class="filename"><a href='download.php?file=miniupnpc-1.7.tar.gz'>miniupnpc-1.7.tar.gz</a></td>
1567 <td class="filesize">68327</td>
1568 <td class="filedate">24/05/2012 18:17:48 +0000</td>
1569 <td class="comment">MiniUPnP client release source code</td>
1570 <td></td>
1571</tr>
1572<tr>
1573 <td class="filename"><a href='download.php?file=minissdpd-1.2.tar.gz'>minissdpd-1.2.tar.gz</a></td>
1574 <td class="filesize">19874</td>
1575 <td class="filedate">24/05/2012 18:06:24 +0000</td>
1576 <td class="comment">MiniSSDPd release source code</td>
1577 <td></td>
1578</tr>
1579<tr>
1580 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120509.tar.gz'>miniupnpd-1.6.20120509.tar.gz</a></td>
1581 <td class="filesize">137147</td>
1582 <td class="filedate">09/05/2012 10:45:44 +0000</td>
1583 <td class="comment">MiniUPnP daemon source code</td>
1584 <td></td>
1585</tr>
1586<tr>
1587 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120509.tar.gz'>miniupnpc-1.6.20120509.tar.gz</a></td>
1588 <td class="filesize">68205</td>
1589 <td class="filedate">09/05/2012 10:45:41 +0000</td>
1590 <td class="comment">MiniUPnP client source code</td>
1591 <td></td>
1592</tr>
1593<tr>
1594 <td class="filename"><a href='download.php?file=minissdpd-1.1.20120509.tar.gz'>minissdpd-1.1.20120509.tar.gz</a></td>
1595 <td class="filesize">18123</td>
1596 <td class="filedate">09/05/2012 10:45:39 +0000</td>
1597 <td class="comment">MiniSSDPd source code</td>
1598 <td></td>
1599</tr>
1600<tr>
1601 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120502.tar.gz'>miniupnpd-1.6.20120502.tar.gz</a></td>
1602 <td class="filesize">136688</td>
1603 <td class="filedate">01/05/2012 22:51:18 +0000</td>
1604 <td class="comment">MiniUPnP daemon source code</td>
1605 <td></td>
1606</tr>
1607<tr>
1608 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120502.tar.gz'>miniupnpc-1.6.20120502.tar.gz</a></td>
1609 <td class="filesize">68170</td>
1610 <td class="filedate">01/05/2012 22:51:11 +0000</td>
1611 <td class="comment">MiniUPnP client source code</td>
1612 <td></td>
1613</tr>
1614<tr>
1615 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120426.tar.gz'>miniupnpd-1.6.20120426.tar.gz</a></td>
1616 <td class="filesize">134764</td>
1617 <td class="filedate">26/04/2012 16:24:29 +0000</td>
1618 <td class="comment">MiniUPnP daemon source code</td>
1619 <td></td>
1620</tr>
1621<tr>
1622 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120424.tar.gz'>miniupnpd-1.6.20120424.tar.gz</a></td>
1623 <td class="filesize">132522</td>
1624 <td class="filedate">23/04/2012 22:43:17 +0000</td>
1625 <td class="comment">MiniUPnP daemon source code</td>
1626 <td></td>
1627</tr>
1628<tr>
1629 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120424.tar.gz'>miniupnpc-1.6.20120424.tar.gz</a></td>
1630 <td class="filesize">68067</td>
1631 <td class="filedate">23/04/2012 22:43:10 +0000</td>
1632 <td class="comment">MiniUPnP client source code</td>
1633 <td></td>
1634</tr>
1635<tr>
1636 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120420.tar.gz'>miniupnpd-1.6.20120420.tar.gz</a></td>
1637 <td class="filesize">131972</td>
1638 <td class="filedate">20/04/2012 14:58:57 +0000</td>
1639 <td class="comment">MiniUPnP daemon source code</td>
1640 <td></td>
1641</tr>
1642<tr>
1643 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120420.tar.gz'>miniupnpc-1.6.20120420.tar.gz</a></td>
1644 <td class="filesize">68068</td>
1645 <td class="filedate">20/04/2012 14:58:39 +0000</td>
1646 <td class="comment">MiniUPnP client source code</td>
1647 <td></td>
1648</tr>
1649<tr>
1650 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120419.tar.gz'>miniupnpd-1.6.20120419.tar.gz</a></td>
1651 <td class="filesize">131088</td>
1652 <td class="filedate">18/04/2012 23:41:36 +0000</td>
1653 <td class="comment">MiniUPnP daemon source code</td>
1654 <td></td>
1655</tr>
1656<tr>
1657 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120418.tar.gz'>miniupnpd-1.6.20120418.tar.gz</a></td>
1658 <td class="filesize">130879</td>
1659 <td class="filedate">18/04/2012 21:01:10 +0000</td>
1660 <td class="comment">MiniUPnP daemon source code</td>
1661 <td></td>
1662</tr>
1663<tr>
1664 <td class="filename"><a href='download.php?file=minissdpd-1.1.20120410.tar.gz'>minissdpd-1.1.20120410.tar.gz</a></td>
1665 <td class="filesize">18059</td>
1666 <td class="filedate">09/04/2012 22:45:38 +0000</td>
1667 <td class="comment">MiniSSDPd source code</td>
1668 <td></td>
1669</tr>
1670<tr>
1671 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120410.tar.gz'>miniupnpc-1.6.20120410.tar.gz</a></td>
1672 <td class="filesize">67934</td>
1673 <td class="filedate">09/04/2012 22:45:10 +0000</td>
1674 <td class="comment">MiniUPnP client source code</td>
1675 <td></td>
1676</tr>
1677<tr>
1678 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120406.tar.gz'>miniupnpd-1.6.20120406.tar.gz</a></td>
1679 <td class="filesize">128992</td>
1680 <td class="filedate">06/04/2012 17:52:57 +0000</td>
1681 <td class="comment">MiniUPnP daemon source code</td>
1682 <td></td>
1683</tr>
1684<tr>
1685 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120320.tar.gz'>miniupnpc-1.6.20120320.tar.gz</a></td>
1686 <td class="filesize">67374</td>
1687 <td class="filedate">20/03/2012 16:55:48 +0000</td>
1688 <td class="comment">MiniUPnP client source code</td>
1689 <td></td>
1690</tr>
1691<tr>
1692 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120320.tar.gz'>miniupnpd-1.6.20120320.tar.gz</a></td>
1693 <td class="filesize">127968</td>
1694 <td class="filedate">20/03/2012 16:46:07 +0000</td>
1695 <td class="comment">MiniUPnP daemon source code</td>
1696 <td></td>
1697</tr>
1698<tr>
1699 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120305.tar.gz'>miniupnpd-1.6.20120305.tar.gz</a></td>
1700 <td class="filesize">126985</td>
1701 <td class="filedate">05/03/2012 20:42:01 +0000</td>
1702 <td class="comment">MiniUPnP daemon source code</td>
1703 <td></td>
1704</tr>
1705<tr>
1706 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120207.tar.gz'>miniupnpd-1.6.20120207.tar.gz</a></td>
1707 <td class="filesize">127425</td>
1708 <td class="filedate">07/02/2012 10:21:16 +0000</td>
1709 <td class="comment">MiniUPnP daemon source code</td>
1710 <td></td>
1711</tr>
1712<tr>
1713 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120203.tar.gz'>miniupnpd-1.6.20120203.tar.gz</a></td>
1714 <td class="filesize">126599</td>
1715 <td class="filedate">03/02/2012 15:14:13 +0000</td>
1716 <td class="comment">MiniUPnP daemon source code</td>
1717 <td></td>
1718</tr>
1719<tr>
1720 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120125.tar.gz'>miniupnpc-1.6.20120125.tar.gz</a></td>
1721 <td class="filesize">67354</td>
1722 <td class="filedate">25/01/2012 21:12:28 +0000</td>
1723 <td class="comment">MiniUPnP client source code</td>
1724 <td></td>
1725</tr>
1726<tr>
1727 <td class="filename"><a href='download.php?file=miniupnpc-1.6.20120121.tar.gz'>miniupnpc-1.6.20120121.tar.gz</a></td>
1728 <td class="filesize">67347</td>
1729 <td class="filedate">21/01/2012 14:07:41 +0000</td>
1730 <td class="comment">MiniUPnP client source code</td>
1731 <td></td>
1732</tr>
1733<tr>
1734 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20120121.tar.gz'>miniupnpd-1.6.20120121.tar.gz</a></td>
1735 <td class="filesize">126021</td>
1736 <td class="filedate">21/01/2012 14:07:33 +0000</td>
1737 <td class="comment">MiniUPnP daemon source code</td>
1738 <td></td>
1739</tr>
1740<tr>
1741 <td class="filename"><a href='download.php?file=minissdpd-1.1.20120121.tar.gz'>minissdpd-1.1.20120121.tar.gz</a></td>
1742 <td class="filesize">17762</td>
1743 <td class="filedate">21/01/2012 14:07:16 +0000</td>
1744 <td class="comment">MiniSSDPd source code</td>
1745 <td></td>
1746</tr>
1747<tr>
1748 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20120121.zip'>upnpc-exe-win32-20120121.zip</a></td>
1749 <td class="filesize">94575</td>
1750 <td class="filedate">21/01/2012 13:59:11 +0000</td>
1751 <td class="comment">Windows executable</td>
1752 <td></td>
1753</tr>
1754<tr>
1755 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20111212.zip'>upnpc-exe-win32-20111212.zip</a></td>
1756 <td class="filesize">94507</td>
1757 <td class="filedate">12/12/2011 12:33:48 +0000</td>
1758 <td class="comment">Windows executable</td>
1759 <td></td>
1760</tr>
1761<tr>
1762 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20111118.tar.gz'>miniupnpd-1.6.20111118.tar.gz</a></td>
1763 <td class="filesize">125683</td>
1764 <td class="filedate">18/11/2011 11:26:12 +0000</td>
1765 <td class="comment">MiniUPnP daemon source code</td>
1766 <td></td>
1767</tr>
1768<tr>
1769 <td class="filename"><a href='download.php?file=minissdpd-1.1.20111007.tar.gz'>minissdpd-1.1.20111007.tar.gz</a></td>
1770 <td class="filesize">17611</td>
1771 <td class="filedate">07/10/2011 09:47:51 +0000</td>
1772 <td class="comment">MiniSSDPd source code</td>
1773 <td></td>
1774</tr>
1775<tr>
1776 <td class="filename"><a href='download.php?file=xchat-upnp20110811.patch'>xchat-upnp20110811.patch</a></td>
1777 <td class="filesize">10329</td>
1778 <td class="filedate">11/08/2011 15:18:25 +0000</td>
1779 <td class="comment">Patch to add UPnP capabilities to xchat</td>
1780 <td></td>
1781</tr>
1782<tr>
1783 <td class="filename"><a href='download.php?file=xchat-upnp20110811-2.8.8.patch'>xchat-upnp20110811-2.8.8.patch</a></td>
1784 <td class="filesize">11529</td>
1785 <td class="filedate">11/08/2011 15:18:23 +0000</td>
1786 <td class="comment">Patch to add UPnP capabilities to xchat</td>
1787 <td></td>
1788</tr>
1789<tr>
1790 <td class="filename"><a href='download.php?file=libnatpmp-20110808.tar.gz'>libnatpmp-20110808.tar.gz</a></td>
1791 <td class="filesize">17762</td>
1792 <td class="filedate">08/08/2011 21:21:34 +0000</td>
1793 <td class="comment">libnatpmp source code</td>
1794 <td></td>
1795</tr>
1796<tr>
1797 <td class="filename"><a href='download.php?file=libnatpmp-20110730.tar.gz'>libnatpmp-20110730.tar.gz</a></td>
1798 <td class="filesize">17687</td>
1799 <td class="filedate">30/07/2011 13:19:31 +0000</td>
1800 <td class="comment">libnatpmp source code</td>
1801 <td></td>
1802</tr>
1803<tr>
1804 <td class="filename"><a href='download.php?file=minissdpd-1.1.tar.gz'>minissdpd-1.1.tar.gz</a></td>
1805 <td class="filesize">17481</td>
1806 <td class="filedate">30/07/2011 13:17:30 +0000</td>
1807 <td class="comment">MiniSSDPd release source code</td>
1808 <td></td>
1809</tr>
1810<tr>
1811 <td class="filename"><a href='download.php?file=miniupnpd-1.6.20110730.tar.gz'>miniupnpd-1.6.20110730.tar.gz</a></td>
1812 <td class="filesize">125583</td>
1813 <td class="filedate">30/07/2011 13:17:09 +0000</td>
1814 <td class="comment">MiniUPnP daemon source code</td>
1815 <td></td>
1816</tr>
1817<tr>
1818 <td class="filename"><a href='download.php?file=minissdpd-1.0.20110729.tar.gz'>minissdpd-1.0.20110729.tar.gz</a></td>
1819 <td class="filesize">15898</td>
1820 <td class="filedate">29/07/2011 08:47:26 +0000</td>
1821 <td class="comment">MiniSSDPd source code</td>
1822 <td></td>
1823</tr>
1824<tr>
1825 <td class="filename"><a href='download.php?file=miniupnpc-1.6.tar.gz'>miniupnpc-1.6.tar.gz</a></td>
1826 <td class="filesize">66454</td>
1827 <td class="filedate">25/07/2011 18:03:09 +0000</td>
1828 <td class="comment">MiniUPnP client release source code</td>
1829 <td></td>
1830</tr>
1831<tr>
1832 <td class="filename"><a href='download.php?file=miniupnpd-1.6.tar.gz'>miniupnpd-1.6.tar.gz</a></td>
1833 <td class="filesize">124917</td>
1834 <td class="filedate">25/07/2011 16:37:57 +0000</td>
1835 <td class="comment">MiniUPnP daemon release source code</td>
1836 <td></td>
1837</tr>
1838<tr>
1839 <td class="filename"><a href='download.php?file=minidlna_1.0.21.minissdp1.patch'>minidlna_1.0.21.minissdp1.patch</a></td>
1840 <td class="filesize">7598</td>
1841 <td class="filedate">25/07/2011 14:57:50 +0000</td>
1842 <td class="comment">Patch for MiniDLNA to use miniSSDPD</td>
1843 <td></td>
1844</tr>
1845<tr>
1846 <td class="filename"><a href='download.php?file=libnatpmp-20110715.tar.gz'>libnatpmp-20110715.tar.gz</a></td>
1847 <td class="filesize">17943</td>
1848 <td class="filedate">15/07/2011 08:31:40 +0000</td>
1849 <td class="comment">libnatpmp source code</td>
1850 <td></td>
1851</tr>
1852<tr>
1853 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110715.tar.gz'>miniupnpd-1.5.20110715.tar.gz</a></td>
1854 <td class="filesize">124519</td>
1855 <td class="filedate">15/07/2011 07:55:17 +0000</td>
1856 <td class="comment">MiniUPnP daemon source code</td>
1857 <td></td>
1858</tr>
1859<tr>
1860 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20110714.zip'>upnpc-exe-win32-20110714.zip</a></td>
1861 <td class="filesize">94236</td>
1862 <td class="filedate">13/07/2011 23:16:01 +0000</td>
1863 <td class="comment">Windows executable</td>
1864 <td></td>
1865</tr>
1866<tr>
1867 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110623.tar.gz'>miniupnpd-1.5.20110623.tar.gz</a></td>
1868 <td class="filesize">123529</td>
1869 <td class="filedate">22/06/2011 22:29:15 +0000</td>
1870 <td class="comment">MiniUPnP daemon source code</td>
1871 <td></td>
1872</tr>
1873<tr>
1874 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110620.tar.gz'>miniupnpd-1.5.20110620.tar.gz</a></td>
1875 <td class="filesize">123221</td>
1876 <td class="filedate">20/06/2011 14:11:11 +0000</td>
1877 <td class="comment">MiniUPnP daemon source code</td>
1878 <td></td>
1879</tr>
1880<tr>
1881 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110618.tar.gz'>miniupnpd-1.5.20110618.tar.gz</a></td>
1882 <td class="filesize">123176</td>
1883 <td class="filedate">17/06/2011 23:29:18 +0000</td>
1884 <td class="comment">MiniUPnP daemon source code</td>
1885 <td></td>
1886</tr>
1887<tr>
1888 <td class="filename"><a href='download.php?file=miniupnpc-1.5.20110618.tar.gz'>miniupnpc-1.5.20110618.tar.gz</a></td>
1889 <td class="filesize">66401</td>
1890 <td class="filedate">17/06/2011 23:29:17 +0000</td>
1891 <td class="comment">MiniUPnP client source code</td>
1892 <td></td>
1893</tr>
1894<tr>
1895 <td class="filename"><a href='download.php?file=libnatpmp-20110618.tar.gz'>libnatpmp-20110618.tar.gz</a></td>
1896 <td class="filesize">17901</td>
1897 <td class="filedate">17/06/2011 23:29:16 +0000</td>
1898 <td class="comment">libnatpmp source code</td>
1899 <td></td>
1900</tr>
1901<tr>
1902 <td class="filename"><a href='download.php?file=minissdpd-1.0.20110618.tar.gz'>minissdpd-1.0.20110618.tar.gz</a></td>
1903 <td class="filesize">15193</td>
1904 <td class="filedate">17/06/2011 23:29:16 +0000</td>
1905 <td class="comment">MiniSSDPd source code</td>
1906 <td></td>
1907</tr>
1908<tr>
1909 <td class="filename" colspan="2"><a href='download.php?file=minidlna_cvs20110529_minissdp1.patch'>minidlna_cvs20110529_minissdp1.patch</a></td>
1910 <td class="filedate">29/05/2011 21:19:09 +0000</td>
1911 <td class="comment">Patch for MiniDLNA to use miniSSDPD</td>
1912 <td></td>
1913</tr>
1914<tr>
1915 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110528.tar.gz'>miniupnpd-1.5.20110528.tar.gz</a></td>
1916 <td class="filesize">121985</td>
1917 <td class="filedate">28/05/2011 09:39:04 +0000</td>
1918 <td class="comment">MiniUPnP daemon source code</td>
1919 <td></td>
1920</tr>
1921<tr>
1922 <td class="filename"><a href='download.php?file=minidlna_1.0.19_minissdp1.patch'>minidlna_1.0.19_minissdp1.patch</a></td>
1923 <td class="filesize">9080</td>
1924 <td class="filedate">27/05/2011 09:55:04 +0000</td>
1925 <td class="comment">Patch for MiniDLNA to use miniSSDPD</td>
1926 <td></td>
1927</tr>
1928<tr>
1929 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110527.tar.gz'>miniupnpd-1.5.20110527.tar.gz</a></td>
1930 <td class="filesize">120896</td>
1931 <td class="filedate">27/05/2011 08:28:35 +0000</td>
1932 <td class="comment">MiniUPnP daemon source code</td>
1933 <td></td>
1934</tr>
1935<tr>
1936 <td class="filename"><a href='download.php?file=miniupnpc-1.5.20110527.tar.gz'>miniupnpc-1.5.20110527.tar.gz</a></td>
1937 <td class="filesize">66279</td>
1938 <td class="filedate">27/05/2011 08:28:34 +0000</td>
1939 <td class="comment">MiniUPnP client source code</td>
1940 <td></td>
1941</tr>
1942<tr>
1943 <td class="filename"><a href='download.php?file=libnatpmp-20110527.tar.gz'>libnatpmp-20110527.tar.gz</a></td>
1944 <td class="filesize">17627</td>
1945 <td class="filedate">27/05/2011 08:28:33 +0000</td>
1946 <td class="comment">libnatpmp source code</td>
1947 <td></td>
1948</tr>
1949<tr>
1950 <td class="filename"><a href='download.php?file=minissdpd-1.0.20110523.tar.gz'>minissdpd-1.0.20110523.tar.gz</a></td>
1951 <td class="filesize">15024</td>
1952 <td class="filedate">23/05/2011 12:55:31 +0000</td>
1953 <td class="comment">MiniSSDPd source code</td>
1954 <td></td>
1955</tr>
1956<tr>
1957 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110520.tar.gz'>miniupnpd-1.5.20110520.tar.gz</a></td>
1958 <td class="filesize">119227</td>
1959 <td class="filedate">20/05/2011 18:00:41 +0000</td>
1960 <td class="comment">MiniUPnP daemon source code</td>
1961 <td></td>
1962</tr>
1963<tr>
1964 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110519.tar.gz'>miniupnpd-1.5.20110519.tar.gz</a></td>
1965 <td class="filesize">114735</td>
1966 <td class="filedate">18/05/2011 22:29:06 +0000</td>
1967 <td class="comment">MiniUPnP daemon source code</td>
1968 <td></td>
1969</tr>
1970<tr>
1971 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110516.tar.gz'>miniupnpd-1.5.20110516.tar.gz</a></td>
1972 <td class="filesize">113348</td>
1973 <td class="filedate">16/05/2011 09:32:51 +0000</td>
1974 <td class="comment">MiniUPnP daemon source code</td>
1975 <td></td>
1976</tr>
1977<tr>
1978 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110515.tar.gz'>miniupnpd-1.5.20110515.tar.gz</a></td>
1979 <td class="filesize">113135</td>
1980 <td class="filedate">15/05/2011 21:51:29 +0000</td>
1981 <td class="comment">MiniUPnP daemon source code</td>
1982 <td></td>
1983</tr>
1984<tr>
1985 <td class="filename"><a href='download.php?file=miniupnpc-1.5.20110515.tar.gz'>miniupnpc-1.5.20110515.tar.gz</a></td>
1986 <td class="filesize">66112</td>
1987 <td class="filedate">15/05/2011 21:51:28 +0000</td>
1988 <td class="comment">MiniUPnP client source code</td>
1989 <td></td>
1990</tr>
1991<tr>
1992 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110513.tar.gz'>miniupnpd-1.5.20110513.tar.gz</a></td>
1993 <td class="filesize">111029</td>
1994 <td class="filedate">13/05/2011 14:03:12 +0000</td>
1995 <td class="comment">MiniUPnP daemon source code</td>
1996 <td></td>
1997</tr>
1998<tr>
1999 <td class="filename"><a href='download.php?file=miniupnpc-1.5.20110506.tar.gz'>miniupnpc-1.5.20110506.tar.gz</a></td>
2000 <td class="filesize">65536</td>
2001 <td class="filedate">06/05/2011 16:35:38 +0000</td>
2002 <td class="comment">MiniUPnP client source code</td>
2003 <td></td>
2004</tr>
2005<tr>
2006 <td class="filename"><a href='download.php?file=miniupnpc-1.4-v6.20100505.zip'>miniupnpc-1.4-v6.20100505.zip</a></td>
2007 <td class="filesize">91833</td>
2008 <td class="filedate">18/04/2011 20:14:11 +0000</td>
2009 <td class="comment"></td>
2010 <td></td>
2011</tr>
2012<tr>
2013 <td class="filename"><a href='download.php?file=miniupnpd-1.4-v6.20100823.zip'>miniupnpd-1.4-v6.20100823.zip</a></td>
2014 <td class="filesize">222235</td>
2015 <td class="filedate">18/04/2011 20:14:07 +0000</td>
2016 <td class="comment"></td>
2017 <td></td>
2018</tr>
2019<tr>
2020 <td class="filename"><a href='download.php?file=miniupnpc-1.5.20110418.tar.gz'>miniupnpc-1.5.20110418.tar.gz</a></td>
2021 <td class="filesize">61820</td>
2022 <td class="filedate">18/04/2011 20:09:22 +0000</td>
2023 <td class="comment">MiniUPnP client source code</td>
2024 <td></td>
2025</tr>
2026<tr>
2027 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20110418.zip'>upnpc-exe-win32-20110418.zip</a></td>
2028 <td class="filesize">94183</td>
2029 <td class="filedate">18/04/2011 17:53:26 +0000</td>
2030 <td class="comment">Windows executable</td>
2031 <td></td>
2032</tr>
2033<tr>
2034 <td class="filename"><a href='download.php?file=miniupnpc-1.5.20110314.tar.gz'>miniupnpc-1.5.20110314.tar.gz</a></td>
2035 <td class="filesize">57210</td>
2036 <td class="filedate">14/03/2011 14:27:29 +0000</td>
2037 <td class="comment">MiniUPnP client source code</td>
2038 <td></td>
2039</tr>
2040<tr>
2041 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110309.tar.gz'>miniupnpd-1.5.20110309.tar.gz</a></td>
2042 <td class="filesize">100073</td>
2043 <td class="filedate">09/03/2011 15:36:12 +0000</td>
2044 <td class="comment">MiniUPnP daemon source code</td>
2045 <td></td>
2046</tr>
2047<tr>
2048 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110302.tar.gz'>miniupnpd-1.5.20110302.tar.gz</a></td>
2049 <td class="filesize">100756</td>
2050 <td class="filedate">02/03/2011 16:17:44 +0000</td>
2051 <td class="comment">MiniUPnP daemon source code</td>
2052 <td></td>
2053</tr>
2054<tr>
2055 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110221.tar.gz'>miniupnpd-1.5.20110221.tar.gz</a></td>
2056 <td class="filesize">100092</td>
2057 <td class="filedate">20/02/2011 23:48:17 +0000</td>
2058 <td class="comment">MiniUPnP daemon source code</td>
2059 <td></td>
2060</tr>
2061<tr>
2062 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20110215.zip'>upnpc-exe-win32-20110215.zip</a></td>
2063 <td class="filesize">55409</td>
2064 <td class="filedate">15/02/2011 23:05:00 +0000</td>
2065 <td class="comment">Windows executable</td>
2066 <td></td>
2067</tr>
2068<tr>
2069 <td class="filename"><a href='download.php?file=miniupnpc-1.5.20110215.tar.gz'>miniupnpc-1.5.20110215.tar.gz</a></td>
2070 <td class="filesize">54880</td>
2071 <td class="filedate">15/02/2011 11:16:04 +0000</td>
2072 <td class="comment">MiniUPnP client source code</td>
2073 <td></td>
2074</tr>
2075<tr>
2076 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110214.tar.gz'>miniupnpd-1.5.20110214.tar.gz</a></td>
2077 <td class="filesize">99629</td>
2078 <td class="filedate">14/02/2011 18:00:43 +0000</td>
2079 <td class="comment">MiniUPnP daemon source code</td>
2080 <td></td>
2081</tr>
2082<tr>
2083 <td class="filename"><a href='download.php?file=minidlna_1.0.18_minissdp1.patch'>minidlna_1.0.18_minissdp1.patch</a></td>
2084 <td class="filesize">9747</td>
2085 <td class="filedate">02/02/2011 15:12:19 +0000</td>
2086 <td class="comment">Patch for MiniDLNA to use miniSSDPD</td>
2087 <td></td>
2088</tr>
2089<tr>
2090 <td class="filename"><a href='download.php?file=miniupnpd-1.5.20110127.tar.gz'>miniupnpd-1.5.20110127.tar.gz</a></td>
2091 <td class="filesize">97421</td>
2092 <td class="filedate">27/01/2011 17:51:25 +0000</td>
2093 <td class="comment">MiniUPnP daemon source code</td>
2094 <td></td>
2095</tr>
2096<tr>
2097 <td class="filename"><a href='download.php?file=miniupnpd-1.5.tar.gz'>miniupnpd-1.5.tar.gz</a></td>
2098 <td class="filesize">98993</td>
2099 <td class="filedate">04/01/2011 09:45:10 +0000</td>
2100 <td class="comment">MiniUPnP daemon release source code</td>
2101 <td></td>
2102</tr>
2103<tr>
2104 <td class="filename"><a href='download.php?file=miniupnpc-1.5.tar.gz'>miniupnpc-1.5.tar.gz</a></td>
2105 <td class="filesize">53309</td>
2106 <td class="filedate">04/01/2011 09:45:06 +0000</td>
2107 <td class="comment">MiniUPnP client release source code</td>
2108 <td></td>
2109</tr>
2110<tr>
2111 <td class="filename"><a href='download.php?file=libnatpmp-20110103.tar.gz'>libnatpmp-20110103.tar.gz</a></td>
2112 <td class="filesize">17529</td>
2113 <td class="filedate">03/01/2011 17:33:16 +0000</td>
2114 <td class="comment">libnatpmp source code</td>
2115 <td></td>
2116</tr>
2117<tr>
2118 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20101221.tar.gz'>miniupnpc-1.4.20101221.tar.gz</a></td>
2119 <td class="filesize">52342</td>
2120 <td class="filedate">21/12/2010 16:15:38 +0000</td>
2121 <td class="comment">MiniUPnP client source code</td>
2122 <td></td>
2123</tr>
2124<tr>
2125 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20101213.zip'>upnpc-exe-win32-20101213.zip</a></td>
2126 <td class="filesize">52359</td>
2127 <td class="filedate">12/12/2010 23:44:01 +0000</td>
2128 <td class="comment">Windows executable</td>
2129 <td></td>
2130</tr>
2131<tr>
2132 <td class="filename"><a href='download.php?file=libnatpmp-20101211.tar.gz'>libnatpmp-20101211.tar.gz</a></td>
2133 <td class="filesize">17324</td>
2134 <td class="filedate">11/12/2010 17:20:36 +0000</td>
2135 <td class="comment">libnatpmp source code</td>
2136 <td></td>
2137</tr>
2138<tr>
2139 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20101209.tar.gz'>miniupnpc-1.4.20101209.tar.gz</a></td>
2140 <td class="filesize">51900</td>
2141 <td class="filedate">09/12/2010 16:17:30 +0000</td>
2142 <td class="comment">MiniUPnP client source code</td>
2143 <td></td>
2144</tr>
2145<tr>
2146 <td class="filename"><a href='download.php?file=miniupnpd-1.4.20100921.tar.gz'>miniupnpd-1.4.20100921.tar.gz</a></td>
2147 <td class="filesize">95483</td>
2148 <td class="filedate">21/09/2010 15:50:00 +0000</td>
2149 <td class="comment">MiniUPnP daemon source code</td>
2150 <td></td>
2151</tr>
2152<tr>
2153 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20100825.zip'>upnpc-exe-win32-20100825.zip</a></td>
2154 <td class="filesize">50636</td>
2155 <td class="filedate">25/08/2010 08:42:59 +0000</td>
2156 <td class="comment">Windows executable</td>
2157 <td></td>
2158</tr>
2159<tr>
2160 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20100609.tar.gz'>miniupnpc-1.4.20100609.tar.gz</a></td>
2161 <td class="filesize">50390</td>
2162 <td class="filedate">09/06/2010 11:03:11 +0000</td>
2163 <td class="comment">MiniUPnP client source code</td>
2164 <td></td>
2165</tr>
2166<tr>
2167 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20100513.zip'>upnpc-exe-win32-20100513.zip</a></td>
2168 <td class="filesize">50950</td>
2169 <td class="filedate">13/05/2010 16:54:33 +0000</td>
2170 <td class="comment">Windows executable</td>
2171 <td></td>
2172</tr>
2173<tr>
2174 <td class="filename"><a href='download.php?file=miniupnpd-1.4.20100511.tar.gz'>miniupnpd-1.4.20100511.tar.gz</a></td>
2175 <td class="filesize">93281</td>
2176 <td class="filedate">11/05/2010 16:22:33 +0000</td>
2177 <td class="comment">MiniUPnP daemon source code</td>
2178 <td></td>
2179</tr>
2180<tr>
2181 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20100418.zip'>upnpc-exe-win32-20100418.zip</a></td>
2182 <td class="filesize">40758</td>
2183 <td class="filedate">17/04/2010 23:00:37 +0000</td>
2184 <td class="comment">Windows executable</td>
2185 <td></td>
2186</tr>
2187<tr>
2188 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20100418.tar.gz'>miniupnpc-1.4.20100418.tar.gz</a></td>
2189 <td class="filesize">50245</td>
2190 <td class="filedate">17/04/2010 22:18:31 +0000</td>
2191 <td class="comment">MiniUPnP client source code</td>
2192 <td></td>
2193</tr>
2194<tr>
2195 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20100412.tar.gz'>miniupnpc-1.4.20100412.tar.gz</a></td>
2196 <td class="filesize">50145</td>
2197 <td class="filedate">12/04/2010 20:42:53 +0000</td>
2198 <td class="comment">MiniUPnP client source code</td>
2199 <td></td>
2200</tr>
2201<tr>
2202 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20100407.tar.gz'>miniupnpc-1.4.20100407.tar.gz</a></td>
2203 <td class="filesize">49756</td>
2204 <td class="filedate">07/04/2010 10:05:08 +0000</td>
2205 <td class="comment">MiniUPnP client source code</td>
2206 <td></td>
2207</tr>
2208<tr>
2209 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20100405.tar.gz'>miniupnpc-1.4.20100405.tar.gz</a></td>
2210 <td class="filesize">49549</td>
2211 <td class="filedate">05/04/2010 14:34:38 +0000</td>
2212 <td class="comment">MiniUPnP client source code</td>
2213 <td></td>
2214</tr>
2215<tr>
2216 <td class="filename"><a href='download.php?file=miniupnpd-1.4.20100308.tar.gz'>miniupnpd-1.4.20100308.tar.gz</a></td>
2217 <td class="filesize">92889</td>
2218 <td class="filedate">08/03/2010 17:18:00 +0000</td>
2219 <td class="comment">MiniUPnP daemon source code</td>
2220 <td></td>
2221</tr>
2222<tr>
2223 <td class="filename"><a href='download.php?file=libnatpmp-20100202.tar.gz'>libnatpmp-20100202.tar.gz</a></td>
2224 <td class="filesize">17231</td>
2225 <td class="filedate">02/02/2010 18:41:13 +0000</td>
2226 <td class="comment">libnatpmp source code</td>
2227 <td></td>
2228</tr>
2229<tr>
2230 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20100202.tar.gz'>miniupnpc-1.4.20100202.tar.gz</a></td>
2231 <td class="filesize">46710</td>
2232 <td class="filedate">02/02/2010 18:41:13 +0000</td>
2233 <td class="comment">MiniUPnP client source code</td>
2234 <td></td>
2235</tr>
2236<tr>
2237 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20100106.tar.gz'>miniupnpc-1.4.20100106.tar.gz</a></td>
2238 <td class="filesize">46659</td>
2239 <td class="filedate">06/01/2010 10:08:21 +0000</td>
2240 <td class="comment">MiniUPnP client source code</td>
2241 <td></td>
2242</tr>
2243<tr>
2244 <td class="filename"><a href='download.php?file=miniupnpd-1.4.20091222.tar.gz'>miniupnpd-1.4.20091222.tar.gz</a></td>
2245 <td class="filesize">90993</td>
2246 <td class="filedate">22/12/2009 17:23:48 +0000</td>
2247 <td class="comment">MiniUPnP daemon source code</td>
2248 <td></td>
2249</tr>
2250<tr>
2251 <td class="filename"><a href='download.php?file=libnatpmp-20091219.tar.gz'>libnatpmp-20091219.tar.gz</a></td>
2252 <td class="filesize">16839</td>
2253 <td class="filedate">19/12/2009 14:35:22 +0000</td>
2254 <td class="comment">libnatpmp source code</td>
2255 <td></td>
2256</tr>
2257<tr>
2258 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20091213.tar.gz'>miniupnpc-1.4.20091213.tar.gz</a></td>
2259 <td class="filesize">46510</td>
2260 <td class="filedate">12/12/2009 23:05:40 +0000</td>
2261 <td class="comment">MiniUPnP client source code</td>
2262 <td></td>
2263</tr>
2264<tr>
2265 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20091211.tar.gz'>miniupnpc-1.4.20091211.tar.gz</a></td>
2266 <td class="filesize">45852</td>
2267 <td class="filedate">11/12/2009 16:43:01 +0000</td>
2268 <td class="comment">MiniUPnP client source code</td>
2269 <td></td>
2270</tr>
2271<tr>
2272 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20091210.zip'>upnpc-exe-win32-20091210.zip</a></td>
2273 <td class="filesize">38666</td>
2274 <td class="filedate">10/12/2009 18:50:27 +0000</td>
2275 <td class="comment">Windows executable</td>
2276 <td></td>
2277</tr>
2278<tr>
2279 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20091208.tar.gz'>miniupnpc-1.4.20091208.tar.gz</a></td>
2280 <td class="filesize">43392</td>
2281 <td class="filedate">08/12/2009 10:58:26 +0000</td>
2282 <td class="comment">MiniUPnP client source code</td>
2283 <td></td>
2284</tr>
2285<tr>
2286 <td class="filename"><a href='download.php?file=miniupnpc-1.4.20091203.tar.gz'>miniupnpc-1.4.20091203.tar.gz</a></td>
2287 <td class="filesize">42040</td>
2288 <td class="filedate">03/12/2009 13:56:28 +0000</td>
2289 <td class="comment">MiniUPnP client source code</td>
2290 <td></td>
2291</tr>
2292<tr>
2293 <td class="filename"><a href='download.php?file=miniupnpd-1.4.20091106.tar.gz'>miniupnpd-1.4.20091106.tar.gz</a></td>
2294 <td class="filesize">90787</td>
2295 <td class="filedate">06/11/2009 21:18:50 +0000</td>
2296 <td class="comment">MiniUPnP daemon source code</td>
2297 <td></td>
2298</tr>
2299<tr>
2300 <td class="filename"><a href='download.php?file=miniupnpd-1.4.tar.gz'>miniupnpd-1.4.tar.gz</a></td>
2301 <td class="filesize">90071</td>
2302 <td class="filedate">30/10/2009 09:20:05 +0000</td>
2303 <td class="comment">MiniUPnP daemon release source code</td>
2304 <td></td>
2305</tr>
2306<tr>
2307 <td class="filename"><a href='download.php?file=miniupnpc-1.4.tar.gz'>miniupnpc-1.4.tar.gz</a></td>
2308 <td class="filesize">41790</td>
2309 <td class="filedate">30/10/2009 09:20:04 +0000</td>
2310 <td class="comment">MiniUPnP client release source code</td>
2311 <td></td>
2312</tr>
2313<tr>
2314 <td class="filename"><a href='download.php?file=miniupnpc-20091016.tar.gz'>miniupnpc-20091016.tar.gz</a></td>
2315 <td class="filesize">41792</td>
2316 <td class="filedate">16/10/2009 09:04:35 +0000</td>
2317 <td class="comment">MiniUPnP client source code</td>
2318 <td></td>
2319</tr>
2320<tr>
2321 <td class="filename"><a href='download.php?file=miniupnpd-20091010.tar.gz'>miniupnpd-20091010.tar.gz</a></td>
2322 <td class="filesize">90043</td>
2323 <td class="filedate">10/10/2009 19:21:30 +0000</td>
2324 <td class="comment">MiniUPnP daemon source code</td>
2325 <td></td>
2326</tr>
2327<tr>
2328 <td class="filename"><a href='download.php?file=miniupnpc-20091010.tar.gz'>miniupnpc-20091010.tar.gz</a></td>
2329 <td class="filesize">41671</td>
2330 <td class="filedate">10/10/2009 19:21:28 +0000</td>
2331 <td class="comment">MiniUPnP client source code</td>
2332 <td></td>
2333</tr>
2334<tr>
2335 <td class="filename"><a href='download.php?file=miniupnpd-20090921.tar.gz'>miniupnpd-20090921.tar.gz</a></td>
2336 <td class="filesize">89476</td>
2337 <td class="filedate">21/09/2009 13:00:04 +0000</td>
2338 <td class="comment">MiniUPnP daemon source code</td>
2339 <td></td>
2340</tr>
2341<tr>
2342 <td class="filename"><a href='download.php?file=miniupnpc-20090921.tar.gz'>miniupnpc-20090921.tar.gz</a></td>
2343 <td class="filesize">41630</td>
2344 <td class="filedate">21/09/2009 13:00:03 +0000</td>
2345 <td class="comment">MiniUPnP client source code</td>
2346 <td></td>
2347</tr>
2348<tr>
2349 <td class="filename"><a href='download.php?file=miniupnpd-20090904.tar.gz'>miniupnpd-20090904.tar.gz</a></td>
2350 <td class="filesize">89344</td>
2351 <td class="filedate">04/09/2009 16:24:26 +0000</td>
2352 <td class="comment">MiniUPnP daemon source code</td>
2353 <td></td>
2354</tr>
2355<tr>
2356 <td class="filename"><a href='download.php?file=miniupnpd-20090820.tar.gz'>miniupnpd-20090820.tar.gz</a></td>
2357 <td class="filesize">89149</td>
2358 <td class="filedate">20/08/2009 09:35:58 +0000</td>
2359 <td class="comment">MiniUPnP daemon source code</td>
2360 <td></td>
2361</tr>
2362<tr>
2363 <td class="filename"><a href='download.php?file=miniupnpc-20090807.tar.gz'>miniupnpc-20090807.tar.gz</a></td>
2364 <td class="filesize">41288</td>
2365 <td class="filedate">07/08/2009 14:46:11 +0000</td>
2366 <td class="comment">MiniUPnP client source code</td>
2367 <td></td>
2368</tr>
2369<tr>
2370 <td class="filename"><a href='download.php?file=miniupnpc-20090729.tar.gz'>miniupnpc-20090729.tar.gz</a></td>
2371 <td class="filesize">40588</td>
2372 <td class="filedate">29/07/2009 08:47:43 +0000</td>
2373 <td class="comment">MiniUPnP client source code</td>
2374 <td></td>
2375</tr>
2376<tr>
2377 <td class="filename"><a href='download.php?file=xchat-upnp20061022.patch'>xchat-upnp20061022.patch</a></td>
2378 <td class="filesize">10258</td>
2379 <td class="filedate">17/07/2009 15:49:46 +0000</td>
2380 <td class="comment">Patch to add UPnP capabilities to xchat</td>
2381 <td></td>
2382</tr>
2383<tr>
2384 <td class="filename"><a href='download.php?file=miniupnpc-20090713.tar.gz'>miniupnpc-20090713.tar.gz</a></td>
2385 <td class="filesize">40206</td>
2386 <td class="filedate">13/07/2009 08:53:49 +0000</td>
2387 <td class="comment">MiniUPnP client source code</td>
2388 <td></td>
2389</tr>
2390<tr>
2391 <td class="filename"><a href='download.php?file=libnatpmp-20090713.tar.gz'>libnatpmp-20090713.tar.gz</a></td>
2392 <td class="filesize">14262</td>
2393 <td class="filedate">13/07/2009 08:53:49 +0000</td>
2394 <td class="comment">libnatpmp source code</td>
2395 <td></td>
2396</tr>
2397<tr>
2398 <td class="filename"><a href='download.php?file=miniupnpd-20090605.tar.gz'>miniupnpd-20090605.tar.gz</a></td>
2399 <td class="filesize">83774</td>
2400 <td class="filedate">04/06/2009 23:32:20 +0000</td>
2401 <td class="comment">MiniUPnP daemon source code</td>
2402 <td></td>
2403</tr>
2404<tr>
2405 <td class="filename"><a href='download.php?file=miniupnpc-20090605.tar.gz'>miniupnpc-20090605.tar.gz</a></td>
2406 <td class="filesize">40077</td>
2407 <td class="filedate">04/06/2009 23:32:16 +0000</td>
2408 <td class="comment">MiniUPnP client source code</td>
2409 <td></td>
2410</tr>
2411<tr>
2412 <td class="filename"><a href='download.php?file=libnatpmp-20090605.tar.gz'>libnatpmp-20090605.tar.gz</a></td>
2413 <td class="filesize">13817</td>
2414 <td class="filedate">04/06/2009 23:32:15 +0000</td>
2415 <td class="comment">libnatpmp source code</td>
2416 <td></td>
2417</tr>
2418<tr>
2419 <td class="filename"><a href='download.php?file=miniupnpd-20090516.tar.gz'>miniupnpd-20090516.tar.gz</a></td>
2420 <td class="filesize">83689</td>
2421 <td class="filedate">16/05/2009 08:47:31 +0000</td>
2422 <td class="comment">MiniUPnP daemon source code</td>
2423 <td></td>
2424</tr>
2425<tr>
2426 <td class="filename"><a href='download.php?file=miniupnpc-1.3.tar.gz'>miniupnpc-1.3.tar.gz</a></td>
2427 <td class="filesize">40058</td>
2428 <td class="filedate">17/04/2009 21:27:55 +0000</td>
2429 <td class="comment">MiniUPnP client release source code</td>
2430 <td></td>
2431</tr>
2432<tr>
2433 <td class="filename"><a href='download.php?file=miniupnpd-1.3.tar.gz'>miniupnpd-1.3.tar.gz</a></td>
2434 <td class="filesize">83464</td>
2435 <td class="filedate">17/04/2009 20:11:21 +0000</td>
2436 <td class="comment">MiniUPnP daemon release source code</td>
2437 <td></td>
2438</tr>
2439<tr>
2440 <td class="filename"><a href='download.php?file=libnatpmp-20090310.tar.gz'>libnatpmp-20090310.tar.gz</a></td>
2441 <td class="filesize">11847</td>
2442 <td class="filedate">10/03/2009 10:19:45 +0000</td>
2443 <td class="comment">libnatpmp source code</td>
2444 <td></td>
2445</tr>
2446<tr>
2447 <td class="filename"><a href='download.php?file=miniupnpd-20090214.tar.gz'>miniupnpd-20090214.tar.gz</a></td>
2448 <td class="filesize">82921</td>
2449 <td class="filedate">14/02/2009 11:27:03 +0000</td>
2450 <td class="comment">MiniUPnP daemon source code</td>
2451 <td></td>
2452</tr>
2453<tr>
2454 <td class="filename"><a href='download.php?file=miniupnpd-20090213.tar.gz'>miniupnpd-20090213.tar.gz</a></td>
2455 <td class="filesize">82594</td>
2456 <td class="filedate">13/02/2009 19:48:01 +0000</td>
2457 <td class="comment">MiniUPnP daemon source code</td>
2458 <td></td>
2459</tr>
2460<tr>
2461 <td class="filename"><a href='download.php?file=libnatpmp-20090129.tar.gz'>libnatpmp-20090129.tar.gz</a></td>
2462 <td class="filesize">11748</td>
2463 <td class="filedate">29/01/2009 21:50:31 +0000</td>
2464 <td class="comment">libnatpmp source code</td>
2465 <td></td>
2466</tr>
2467<tr>
2468 <td class="filename"><a href='download.php?file=miniupnpc-20090129.tar.gz'>miniupnpc-20090129.tar.gz</a></td>
2469 <td class="filesize">39976</td>
2470 <td class="filedate">29/01/2009 21:50:30 +0000</td>
2471 <td class="comment">MiniUPnP client source code</td>
2472 <td></td>
2473</tr>
2474<tr>
2475 <td class="filename"><a href='download.php?file=miniupnpd-20090129.tar.gz'>miniupnpd-20090129.tar.gz</a></td>
2476 <td class="filesize">82487</td>
2477 <td class="filedate">29/01/2009 21:50:27 +0000</td>
2478 <td class="comment">MiniUPnP daemon source code</td>
2479 <td></td>
2480</tr>
2481<tr>
2482 <td class="filename"><a href='download.php?file=miniupnpd-20081009.tar.gz'>miniupnpd-20081009.tar.gz</a></td>
2483 <td class="filesize">81732</td>
2484 <td class="filedate">09/10/2008 12:53:02 +0000</td>
2485 <td class="comment">MiniUPnP daemon source code</td>
2486 <td></td>
2487</tr>
2488<tr>
2489 <td class="filename"><a href='download.php?file=minissdpd-1.0.tar.gz'>minissdpd-1.0.tar.gz</a></td>
2490 <td class="filesize">12996</td>
2491 <td class="filedate">07/10/2008 14:03:49 +0000</td>
2492 <td class="comment">MiniSSDPd release source code</td>
2493 <td></td>
2494</tr>
2495<tr>
2496 <td class="filename"><a href='download.php?file=miniupnpc-1.2.tar.gz'>miniupnpc-1.2.tar.gz</a></td>
2497 <td class="filesize">38787</td>
2498 <td class="filedate">07/10/2008 14:03:47 +0000</td>
2499 <td class="comment">MiniUPnP client release source code</td>
2500 <td></td>
2501</tr>
2502<tr>
2503 <td class="filename"><a href='download.php?file=miniupnpd-1.2.tar.gz'>miniupnpd-1.2.tar.gz</a></td>
2504 <td class="filesize">81025</td>
2505 <td class="filedate">07/10/2008 14:03:45 +0000</td>
2506 <td class="comment">MiniUPnP daemon release source code</td>
2507 <td></td>
2508</tr>
2509<tr>
2510 <td class="filename"><a href='download.php?file=miniupnpd-20081006.tar.gz'>miniupnpd-20081006.tar.gz</a></td>
2511 <td class="filesize">80510</td>
2512 <td class="filedate">06/10/2008 15:50:34 +0000</td>
2513 <td class="comment">MiniUPnP daemon source code</td>
2514 <td></td>
2515</tr>
2516<tr>
2517 <td class="filename"><a href='download.php?file=minissdpd-20081006.tar.gz'>minissdpd-20081006.tar.gz</a></td>
2518 <td class="filesize">12230</td>
2519 <td class="filedate">06/10/2008 15:50:33 +0000</td>
2520 <td class="comment">MiniSSDPd source code</td>
2521 <td></td>
2522</tr>
2523<tr>
2524 <td class="filename"><a href='download.php?file=libnatpmp-20081006.tar.gz'>libnatpmp-20081006.tar.gz</a></td>
2525 <td class="filesize">11710</td>
2526 <td class="filedate">06/10/2008 15:50:31 +0000</td>
2527 <td class="comment">libnatpmp source code</td>
2528 <td></td>
2529</tr>
2530<tr>
2531 <td class="filename" colspan="2"><a href='download.php?file=mediatomb_minissdp-20081006.patch'>mediatomb_minissdp-20081006.patch</a></td>
2532 <td class="filedate">06/10/2008 15:48:18 +0000</td>
2533 <td class="comment"></td>
2534 <td></td>
2535</tr>
2536<tr>
2537 <td class="filename"><a href='download.php?file=miniupnpc-20081002.tar.gz'>miniupnpc-20081002.tar.gz</a></td>
2538 <td class="filesize">38291</td>
2539 <td class="filedate">02/10/2008 09:20:18 +0000</td>
2540 <td class="comment">MiniUPnP client source code</td>
2541 <td></td>
2542</tr>
2543<tr>
2544 <td class="filename"><a href='download.php?file=miniupnpd-20081001.tar.gz'>miniupnpd-20081001.tar.gz</a></td>
2545 <td class="filesize">79696</td>
2546 <td class="filedate">01/10/2008 13:11:20 +0000</td>
2547 <td class="comment">MiniUPnP daemon source code</td>
2548 <td></td>
2549</tr>
2550<tr>
2551 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20080925.zip'>upnpc-exe-win32-20080925.zip</a></td>
2552 <td class="filesize">36602</td>
2553 <td class="filedate">25/09/2008 06:59:33 +0000</td>
2554 <td class="comment">Windows executable</td>
2555 <td></td>
2556</tr>
2557<tr>
2558 <td class="filename"><a href='download.php?file=miniupnpd-20080710.tar.gz'>miniupnpd-20080710.tar.gz</a></td>
2559 <td class="filesize">78898</td>
2560 <td class="filedate">10/07/2008 09:38:41 +0000</td>
2561 <td class="comment">MiniUPnP daemon source code</td>
2562 <td></td>
2563</tr>
2564<tr>
2565 <td class="filename"><a href='download.php?file=libnatpmp-20080707.tar.gz'>libnatpmp-20080707.tar.gz</a></td>
2566 <td class="filesize">11679</td>
2567 <td class="filedate">06/07/2008 22:05:23 +0000</td>
2568 <td class="comment">libnatpmp source code</td>
2569 <td></td>
2570</tr>
2571<tr>
2572 <td class="filename"><a href='download.php?file=miniupnpc-1.1.tar.gz'>miniupnpc-1.1.tar.gz</a></td>
2573 <td class="filesize">38235</td>
2574 <td class="filedate">04/07/2008 16:45:24 +0000</td>
2575 <td class="comment">MiniUPnP client release source code</td>
2576 <td></td>
2577</tr>
2578<tr>
2579 <td class="filename"><a href='download.php?file=miniupnpc-20080703.tar.gz'>miniupnpc-20080703.tar.gz</a></td>
2580 <td class="filesize">38204</td>
2581 <td class="filedate">03/07/2008 15:47:37 +0000</td>
2582 <td class="comment">MiniUPnP client source code</td>
2583 <td></td>
2584</tr>
2585<tr>
2586 <td class="filename"><a href='download.php?file=libnatpmp-20080703.tar.gz'>libnatpmp-20080703.tar.gz</a></td>
2587 <td class="filesize">11570</td>
2588 <td class="filedate">03/07/2008 15:47:25 +0000</td>
2589 <td class="comment">libnatpmp source code</td>
2590 <td></td>
2591</tr>
2592<tr>
2593 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20080703.zip'>upnpc-exe-win32-20080703.zip</a></td>
2594 <td class="filesize">36137</td>
2595 <td class="filedate">02/07/2008 23:35:14 +0000</td>
2596 <td class="comment">Windows executable</td>
2597 <td></td>
2598</tr>
2599<tr>
2600 <td class="filename"><a href='download.php?file=libnatpmp-20080702.tar.gz'>libnatpmp-20080702.tar.gz</a></td>
2601 <td class="filesize">8873</td>
2602 <td class="filedate">02/07/2008 17:32:35 +0000</td>
2603 <td class="comment">libnatpmp source code</td>
2604 <td></td>
2605</tr>
2606<tr>
2607 <td class="filename"><a href='download.php?file=libnatpmp-20080630.tar.gz'>libnatpmp-20080630.tar.gz</a></td>
2608 <td class="filesize">8864</td>
2609 <td class="filedate">30/06/2008 14:20:16 +0000</td>
2610 <td class="comment">libnatpmp source code</td>
2611 <td></td>
2612</tr>
2613<tr>
2614 <td class="filename"><a href='download.php?file=libnatpmp-20080529.tar.gz'>libnatpmp-20080529.tar.gz</a></td>
2615 <td class="filesize">7397</td>
2616 <td class="filedate">29/05/2008 09:06:25 +0000</td>
2617 <td class="comment">libnatpmp source code</td>
2618 <td></td>
2619</tr>
2620<tr>
2621 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20080514.zip'>upnpc-exe-win32-20080514.zip</a></td>
2622 <td class="filesize">14227</td>
2623 <td class="filedate">14/05/2008 20:23:19 +0000</td>
2624 <td class="comment">Windows executable</td>
2625 <td></td>
2626</tr>
2627<tr>
2628 <td class="filename"><a href='download.php?file=libnatpmp-20080428.tar.gz'>libnatpmp-20080428.tar.gz</a></td>
2629 <td class="filesize">7295</td>
2630 <td class="filedate">28/04/2008 03:09:14 +0000</td>
2631 <td class="comment">libnatpmp source code</td>
2632 <td></td>
2633</tr>
2634<tr>
2635 <td class="filename"><a href='download.php?file=miniupnpd-20080427.tar.gz'>miniupnpd-20080427.tar.gz</a></td>
2636 <td class="filesize">78765</td>
2637 <td class="filedate">27/04/2008 18:16:36 +0000</td>
2638 <td class="comment">MiniUPnP daemon source code</td>
2639 <td></td>
2640</tr>
2641<tr>
2642 <td class="filename"><a href='download.php?file=miniupnpc-20080427.tar.gz'>miniupnpc-20080427.tar.gz</a></td>
2643 <td class="filesize">37610</td>
2644 <td class="filedate">27/04/2008 18:16:35 +0000</td>
2645 <td class="comment">MiniUPnP client source code</td>
2646 <td></td>
2647</tr>
2648<tr>
2649 <td class="filename"><a href='download.php?file=miniupnpd-1.1.tar.gz'>miniupnpd-1.1.tar.gz</a></td>
2650 <td class="filesize">78594</td>
2651 <td class="filedate">25/04/2008 17:38:05 +0000</td>
2652 <td class="comment">MiniUPnP daemon release source code</td>
2653 <td></td>
2654</tr>
2655<tr>
2656 <td class="filename"><a href='download.php?file=miniupnpc-20080423.tar.gz'>miniupnpc-20080423.tar.gz</a></td>
2657 <td class="filesize">36818</td>
2658 <td class="filedate">23/04/2008 11:57:36 +0000</td>
2659 <td class="comment">MiniUPnP client source code</td>
2660 <td></td>
2661</tr>
2662<tr>
2663 <td class="filename"><a href='download.php?file=miniupnpd-20080308.tar.gz'>miniupnpd-20080308.tar.gz</a></td>
2664 <td class="filesize">75679</td>
2665 <td class="filedate">08/03/2008 11:13:29 +0000</td>
2666 <td class="comment">MiniUPnP daemon source code</td>
2667 <td></td>
2668</tr>
2669<tr>
2670 <td class="filename"><a href='download.php?file=miniupnpd-20080303.tar.gz'>miniupnpd-20080303.tar.gz</a></td>
2671 <td class="filesize">74202</td>
2672 <td class="filedate">03/03/2008 01:43:16 +0000</td>
2673 <td class="comment">MiniUPnP daemon source code</td>
2674 <td></td>
2675</tr>
2676<tr>
2677 <td class="filename"><a href='download.php?file=miniupnpd-20080224.tar.gz'>miniupnpd-20080224.tar.gz</a></td>
2678 <td class="filesize">72773</td>
2679 <td class="filedate">24/02/2008 11:23:17 +0000</td>
2680 <td class="comment">MiniUPnP daemon source code</td>
2681 <td></td>
2682</tr>
2683<tr>
2684 <td class="filename"><a href='download.php?file=miniupnpc-1.0.tar.gz'>miniupnpc-1.0.tar.gz</a></td>
2685 <td class="filesize">36223</td>
2686 <td class="filedate">21/02/2008 13:26:46 +0000</td>
2687 <td class="comment">MiniUPnP client release source code</td>
2688 <td></td>
2689</tr>
2690<tr>
2691 <td class="filename"><a href='download.php?file=miniupnpd-20080221.tar.gz'>miniupnpd-20080221.tar.gz</a></td>
2692 <td class="filesize">70823</td>
2693 <td class="filedate">21/02/2008 10:23:46 +0000</td>
2694 <td class="comment">MiniUPnP daemon source code</td>
2695 <td></td>
2696</tr>
2697<tr>
2698 <td class="filename"><a href='download.php?file=miniupnpc-20080217.tar.gz'>miniupnpc-20080217.tar.gz</a></td>
2699 <td class="filesize">35243</td>
2700 <td class="filedate">16/02/2008 23:47:59 +0000</td>
2701 <td class="comment">MiniUPnP client source code</td>
2702 <td></td>
2703</tr>
2704<tr>
2705 <td class="filename"><a href='download.php?file=miniupnpd-20080207.tar.gz'>miniupnpd-20080207.tar.gz</a></td>
2706 <td class="filesize">70647</td>
2707 <td class="filedate">07/02/2008 21:21:00 +0000</td>
2708 <td class="comment">MiniUPnP daemon source code</td>
2709 <td></td>
2710</tr>
2711<tr>
2712 <td class="filename"><a href='download.php?file=miniupnpc-20080203.tar.gz'>miniupnpc-20080203.tar.gz</a></td>
2713 <td class="filesize">34921</td>
2714 <td class="filedate">03/02/2008 22:28:11 +0000</td>
2715 <td class="comment">MiniUPnP client source code</td>
2716 <td></td>
2717</tr>
2718<tr>
2719 <td class="filename"><a href='download.php?file=miniupnpd-1.0.tar.gz'>miniupnpd-1.0.tar.gz</a></td>
2720 <td class="filesize">69427</td>
2721 <td class="filedate">27/01/2008 22:41:25 +0000</td>
2722 <td class="comment">MiniUPnP daemon release source code</td>
2723 <td></td>
2724</tr>
2725<tr>
2726 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20080118.zip'>upnpc-exe-win32-20080118.zip</a></td>
2727 <td class="filesize">13582</td>
2728 <td class="filedate">18/01/2008 11:42:16 +0000</td>
2729 <td class="comment">Windows executable</td>
2730 <td></td>
2731</tr>
2732<tr>
2733 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC13.tar.gz'>miniupnpd-1.0-RC13.tar.gz</a></td>
2734 <td class="filesize">67892</td>
2735 <td class="filedate">03/01/2008 16:50:21 +0000</td>
2736 <td class="comment">MiniUPnP daemon release source code</td>
2737 <td></td>
2738</tr>
2739<tr>
2740 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC13.tar.gz'>miniupnpc-1.0-RC13.tar.gz</a></td>
2741 <td class="filesize">34820</td>
2742 <td class="filedate">03/01/2008 16:50:20 +0000</td>
2743 <td class="comment">MiniUPnP client release source code</td>
2744 <td></td>
2745</tr>
2746<tr>
2747 <td class="filename"><a href='download.php?file=miniupnpd-20071220.tar.gz'>miniupnpd-20071220.tar.gz</a></td>
2748 <td class="filesize">67211</td>
2749 <td class="filedate">20/12/2007 12:08:34 +0000</td>
2750 <td class="comment">MiniUPnP daemon source code</td>
2751 <td></td>
2752</tr>
2753<tr>
2754 <td class="filename"><a href='download.php?file=miniupnpc-20071219.tar.gz'>miniupnpc-20071219.tar.gz</a></td>
2755 <td class="filesize">34290</td>
2756 <td class="filedate">19/12/2007 18:31:47 +0000</td>
2757 <td class="comment">MiniUPnP client source code</td>
2758 <td></td>
2759</tr>
2760<tr>
2761 <td class="filename"><a href='download.php?file=minissdpd-1.0-RC12.tar.gz'>minissdpd-1.0-RC12.tar.gz</a></td>
2762 <td class="filesize">9956</td>
2763 <td class="filedate">19/12/2007 18:30:12 +0000</td>
2764 <td class="comment">MiniSSDPd release source code</td>
2765 <td></td>
2766</tr>
2767<tr>
2768 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC12.tar.gz'>miniupnpd-1.0-RC12.tar.gz</a></td>
2769 <td class="filesize">66911</td>
2770 <td class="filedate">14/12/2007 17:39:20 +0000</td>
2771 <td class="comment">MiniUPnP daemon release source code</td>
2772 <td></td>
2773</tr>
2774<tr>
2775 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC12.tar.gz'>miniupnpc-1.0-RC12.tar.gz</a></td>
2776 <td class="filesize">32543</td>
2777 <td class="filedate">14/12/2007 17:39:19 +0000</td>
2778 <td class="comment">MiniUPnP client release source code</td>
2779 <td></td>
2780</tr>
2781<tr>
2782 <td class="filename"><a href='download.php?file=miniupnpc-20071213.tar.gz'>miniupnpc-20071213.tar.gz</a></td>
2783 <td class="filesize">32541</td>
2784 <td class="filedate">13/12/2007 17:09:51 +0000</td>
2785 <td class="comment">MiniUPnP client source code</td>
2786 <td></td>
2787</tr>
2788<tr>
2789 <td class="filename"><a href='download.php?file=miniupnpd-20071213.tar.gz'>miniupnpd-20071213.tar.gz</a></td>
2790 <td class="filesize">66826</td>
2791 <td class="filedate">13/12/2007 16:42:50 +0000</td>
2792 <td class="comment">MiniUPnP daemon source code</td>
2793 <td></td>
2794</tr>
2795<tr>
2796 <td class="filename"><a href='download.php?file=libnatpmp-20071213.tar.gz'>libnatpmp-20071213.tar.gz</a></td>
2797 <td class="filesize">5997</td>
2798 <td class="filedate">13/12/2007 14:56:30 +0000</td>
2799 <td class="comment">libnatpmp source code</td>
2800 <td></td>
2801</tr>
2802<tr>
2803 <td class="filename"><a href='download.php?file=libnatpmp-20071202.tar.gz'>libnatpmp-20071202.tar.gz</a></td>
2804 <td class="filesize">5664</td>
2805 <td class="filedate">02/12/2007 00:15:28 +0000</td>
2806 <td class="comment">libnatpmp source code</td>
2807 <td></td>
2808</tr>
2809<tr>
2810 <td class="filename"><a href='download.php?file=miniupnpd-20071103.tar.gz'>miniupnpd-20071103.tar.gz</a></td>
2811 <td class="filesize">65740</td>
2812 <td class="filedate">02/11/2007 23:58:38 +0000</td>
2813 <td class="comment">MiniUPnP daemon source code</td>
2814 <td></td>
2815</tr>
2816<tr>
2817 <td class="filename"><a href='download.php?file=miniupnpd-20071102.tar.gz'>miniupnpd-20071102.tar.gz</a></td>
2818 <td class="filesize">65733</td>
2819 <td class="filedate">02/11/2007 23:05:44 +0000</td>
2820 <td class="comment">MiniUPnP daemon source code</td>
2821 <td></td>
2822</tr>
2823<tr>
2824 <td class="filename"><a href='download.php?file=miniupnpc-20071103.tar.gz'>miniupnpc-20071103.tar.gz</a></td>
2825 <td class="filesize">32239</td>
2826 <td class="filedate">02/11/2007 23:05:34 +0000</td>
2827 <td class="comment">MiniUPnP client source code</td>
2828 <td></td>
2829</tr>
2830<tr>
2831 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC11.tar.gz'>miniupnpd-1.0-RC11.tar.gz</a></td>
2832 <td class="filesize">64828</td>
2833 <td class="filedate">25/10/2007 13:27:18 +0000</td>
2834 <td class="comment">MiniUPnP daemon release source code</td>
2835 <td></td>
2836</tr>
2837<tr>
2838 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC11.tar.gz'>miniupnpc-1.0-RC11.tar.gz</a></td>
2839 <td class="filesize">32161</td>
2840 <td class="filedate">25/10/2007 13:27:17 +0000</td>
2841 <td class="comment">MiniUPnP client release source code</td>
2842 <td></td>
2843</tr>
2844<tr>
2845 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20071025.zip'>upnpc-exe-win32-20071025.zip</a></td>
2846 <td class="filesize">12809</td>
2847 <td class="filedate">24/10/2007 23:15:55 +0000</td>
2848 <td class="comment">Windows executable</td>
2849 <td></td>
2850</tr>
2851<tr>
2852 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC10.tar.gz'>miniupnpd-1.0-RC10.tar.gz</a></td>
2853 <td class="filesize">62674</td>
2854 <td class="filedate">12/10/2007 08:38:33 +0000</td>
2855 <td class="comment">MiniUPnP daemon release source code</td>
2856 <td></td>
2857</tr>
2858<tr>
2859 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC10.tar.gz'>miniupnpc-1.0-RC10.tar.gz</a></td>
2860 <td class="filesize">31962</td>
2861 <td class="filedate">12/10/2007 08:38:31 +0000</td>
2862 <td class="comment">MiniUPnP client release source code</td>
2863 <td></td>
2864</tr>
2865<tr>
2866 <td class="filename"><a href='download.php?file=minissdpd-1.0-RC10.tar.gz'>minissdpd-1.0-RC10.tar.gz</a></td>
2867 <td class="filesize">9517</td>
2868 <td class="filedate">12/10/2007 08:38:30 +0000</td>
2869 <td class="comment">MiniSSDPd release source code</td>
2870 <td></td>
2871</tr>
2872<tr>
2873 <td class="filename"><a href='download.php?file=miniupnpc-20071003.tar.gz'>miniupnpc-20071003.tar.gz</a></td>
2874 <td class="filesize">31199</td>
2875 <td class="filedate">03/10/2007 15:30:13 +0000</td>
2876 <td class="comment">MiniUPnP client source code</td>
2877 <td></td>
2878</tr>
2879<tr>
2880 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20071001.zip'>upnpc-exe-win32-20071001.zip</a></td>
2881 <td class="filesize">12604</td>
2882 <td class="filedate">01/10/2007 17:09:22 +0000</td>
2883 <td class="comment">Windows executable</td>
2884 <td></td>
2885</tr>
2886<tr>
2887 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC9.tar.gz'>miniupnpd-1.0-RC9.tar.gz</a></td>
2888 <td class="filesize">54778</td>
2889 <td class="filedate">27/09/2007 19:38:36 +0000</td>
2890 <td class="comment">MiniUPnP daemon release source code</td>
2891 <td></td>
2892</tr>
2893<tr>
2894 <td class="filename"><a href='download.php?file=minissdpd-1.0-RC9.tar.gz'>minissdpd-1.0-RC9.tar.gz</a></td>
2895 <td class="filesize">9163</td>
2896 <td class="filedate">27/09/2007 17:00:03 +0000</td>
2897 <td class="comment">MiniSSDPd release source code</td>
2898 <td></td>
2899</tr>
2900<tr>
2901 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC9.tar.gz'>miniupnpc-1.0-RC9.tar.gz</a></td>
2902 <td class="filesize">30538</td>
2903 <td class="filedate">27/09/2007 17:00:03 +0000</td>
2904 <td class="comment">MiniUPnP client release source code</td>
2905 <td></td>
2906</tr>
2907<tr>
2908 <td class="filename"><a href='download.php?file=miniupnpd-20070924.tar.gz'>miniupnpd-20070924.tar.gz</a></td>
2909 <td class="filesize">52338</td>
2910 <td class="filedate">24/09/2007 20:26:05 +0000</td>
2911 <td class="comment">MiniUPnP daemon source code</td>
2912 <td></td>
2913</tr>
2914<tr>
2915 <td class="filename"><a href='download.php?file=miniupnpd-20070923.tar.gz'>miniupnpd-20070923.tar.gz</a></td>
2916 <td class="filesize">51060</td>
2917 <td class="filedate">23/09/2007 21:13:34 +0000</td>
2918 <td class="comment">MiniUPnP daemon source code</td>
2919 <td></td>
2920</tr>
2921<tr>
2922 <td class="filename"><a href='download.php?file=miniupnpc-20070923.tar.gz'>miniupnpc-20070923.tar.gz</a></td>
2923 <td class="filesize">30246</td>
2924 <td class="filedate">23/09/2007 21:13:33 +0000</td>
2925 <td class="comment">MiniUPnP client source code</td>
2926 <td></td>
2927</tr>
2928<tr>
2929 <td class="filename"><a href='download.php?file=minissdpd-20070923.tar.gz'>minissdpd-20070923.tar.gz</a></td>
2930 <td class="filesize">8978</td>
2931 <td class="filedate">23/09/2007 21:13:32 +0000</td>
2932 <td class="comment">MiniSSDPd source code</td>
2933 <td></td>
2934</tr>
2935<tr>
2936 <td class="filename"><a href='download.php?file=miniupnpc-20070902.tar.gz'>miniupnpc-20070902.tar.gz</a></td>
2937 <td class="filesize">30205</td>
2938 <td class="filedate">01/09/2007 23:47:23 +0000</td>
2939 <td class="comment">MiniUPnP client source code</td>
2940 <td></td>
2941</tr>
2942<tr>
2943 <td class="filename"><a href='download.php?file=minissdpd-20070902.tar.gz'>minissdpd-20070902.tar.gz</a></td>
2944 <td class="filesize">6539</td>
2945 <td class="filedate">01/09/2007 23:47:20 +0000</td>
2946 <td class="comment">MiniSSDPd source code</td>
2947 <td></td>
2948</tr>
2949<tr>
2950 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC8.tar.gz'>miniupnpd-1.0-RC8.tar.gz</a></td>
2951 <td class="filesize">50952</td>
2952 <td class="filedate">29/08/2007 10:56:09 +0000</td>
2953 <td class="comment">MiniUPnP daemon release source code</td>
2954 <td></td>
2955</tr>
2956<tr>
2957 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC8.tar.gz'>miniupnpc-1.0-RC8.tar.gz</a></td>
2958 <td class="filesize">29312</td>
2959 <td class="filedate">29/08/2007 10:56:08 +0000</td>
2960 <td class="comment">MiniUPnP client release source code</td>
2961 <td></td>
2962</tr>
2963<tr>
2964 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC7.tar.gz'>miniupnpd-1.0-RC7.tar.gz</a></td>
2965 <td class="filesize">50613</td>
2966 <td class="filedate">20/07/2007 00:15:45 +0000</td>
2967 <td class="comment">MiniUPnP daemon release source code</td>
2968 <td></td>
2969</tr>
2970<tr>
2971 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC6.tar.gz'>miniupnpd-1.0-RC6.tar.gz</a></td>
2972 <td class="filesize">49986</td>
2973 <td class="filedate">12/06/2007 17:12:07 +0000</td>
2974 <td class="comment">MiniUPnP daemon release source code</td>
2975 <td></td>
2976</tr>
2977<tr>
2978 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC6.tar.gz'>miniupnpc-1.0-RC6.tar.gz</a></td>
2979 <td class="filesize">29032</td>
2980 <td class="filedate">12/06/2007 17:12:06 +0000</td>
2981 <td class="comment">MiniUPnP client release source code</td>
2982 <td></td>
2983</tr>
2984<tr>
2985 <td class="filename"><a href='download.php?file=miniupnpd-20070607.tar.gz'>miniupnpd-20070607.tar.gz</a></td>
2986 <td class="filesize">49768</td>
2987 <td class="filedate">06/06/2007 23:12:00 +0000</td>
2988 <td class="comment">MiniUPnP daemon source code</td>
2989 <td></td>
2990</tr>
2991<tr>
2992 <td class="filename"><a href='download.php?file=miniupnpd-20070605.tar.gz'>miniupnpd-20070605.tar.gz</a></td>
2993 <td class="filesize">49710</td>
2994 <td class="filedate">05/06/2007 21:01:53 +0000</td>
2995 <td class="comment">MiniUPnP daemon source code</td>
2996 <td></td>
2997</tr>
2998<tr>
2999 <td class="filename"><a href='download.php?file=miniupnpd-20070521.tar.gz'>miniupnpd-20070521.tar.gz</a></td>
3000 <td class="filesize">48374</td>
3001 <td class="filedate">21/05/2007 13:07:43 +0000</td>
3002 <td class="comment">MiniUPnP daemon source code</td>
3003 <td></td>
3004</tr>
3005<tr>
3006 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20070519.zip'>upnpc-exe-win32-20070519.zip</a></td>
3007 <td class="filesize">10836</td>
3008 <td class="filedate">19/05/2007 13:14:15 +0000</td>
3009 <td class="comment">Windows executable</td>
3010 <td></td>
3011</tr>
3012<tr>
3013 <td class="filename"><a href='download.php?file=miniupnpc-20070515.tar.gz'>miniupnpc-20070515.tar.gz</a></td>
3014 <td class="filesize">25802</td>
3015 <td class="filedate">15/05/2007 18:15:25 +0000</td>
3016 <td class="comment">MiniUPnP client source code</td>
3017 <td></td>
3018</tr>
3019<tr>
3020 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC5.tar.gz'>miniupnpd-1.0-RC5.tar.gz</a></td>
3021 <td class="filesize">48064</td>
3022 <td class="filedate">10/05/2007 20:22:48 +0000</td>
3023 <td class="comment">MiniUPnP daemon release source code</td>
3024 <td></td>
3025</tr>
3026<tr>
3027 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC5.tar.gz'>miniupnpc-1.0-RC5.tar.gz</a></td>
3028 <td class="filesize">25242</td>
3029 <td class="filedate">10/05/2007 20:22:46 +0000</td>
3030 <td class="comment">MiniUPnP client release source code</td>
3031 <td></td>
3032</tr>
3033<tr>
3034 <td class="filename"><a href='download.php?file=miniupnpd-20070412.tar.gz'>miniupnpd-20070412.tar.gz</a></td>
3035 <td class="filesize">47807</td>
3036 <td class="filedate">12/04/2007 20:21:48 +0000</td>
3037 <td class="comment">MiniUPnP daemon source code</td>
3038 <td></td>
3039</tr>
3040<tr>
3041 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC4.tar.gz'>miniupnpd-1.0-RC4.tar.gz</a></td>
3042 <td class="filesize">47687</td>
3043 <td class="filedate">17/03/2007 11:43:13 +0000</td>
3044 <td class="comment">MiniUPnP daemon release source code</td>
3045 <td></td>
3046</tr>
3047<tr>
3048 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC4.tar.gz'>miniupnpc-1.0-RC4.tar.gz</a></td>
3049 <td class="filesize">25085</td>
3050 <td class="filedate">17/03/2007 11:43:10 +0000</td>
3051 <td class="comment">MiniUPnP client release source code</td>
3052 <td></td>
3053</tr>
3054<tr>
3055 <td class="filename"><a href='download.php?file=miniupnpd-20070311.tar.gz'>miniupnpd-20070311.tar.gz</a></td>
3056 <td class="filesize">47599</td>
3057 <td class="filedate">11/03/2007 00:25:26 +0000</td>
3058 <td class="comment">MiniUPnP daemon source code</td>
3059 <td></td>
3060</tr>
3061<tr>
3062 <td class="filename"><a href='download.php?file=miniupnpd-20070208.tar.gz'>miniupnpd-20070208.tar.gz</a></td>
3063 <td class="filesize">45084</td>
3064 <td class="filedate">07/02/2007 23:04:06 +0000</td>
3065 <td class="comment">MiniUPnP daemon source code</td>
3066 <td></td>
3067</tr>
3068<tr>
3069 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC3.tar.gz'>miniupnpd-1.0-RC3.tar.gz</a></td>
3070 <td class="filesize">44683</td>
3071 <td class="filedate">30/01/2007 23:00:44 +0000</td>
3072 <td class="comment">MiniUPnP daemon release source code</td>
3073 <td></td>
3074</tr>
3075<tr>
3076 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC3.tar.gz'>miniupnpc-1.0-RC3.tar.gz</a></td>
3077 <td class="filesize">25055</td>
3078 <td class="filedate">30/01/2007 23:00:42 +0000</td>
3079 <td class="comment">MiniUPnP client release source code</td>
3080 <td></td>
3081</tr>
3082<tr>
3083 <td class="filename"><a href='download.php?file=miniupnpd-20070130.tar.gz'>miniupnpd-20070130.tar.gz</a></td>
3084 <td class="filesize">43735</td>
3085 <td class="filedate">29/01/2007 23:26:16 +0000</td>
3086 <td class="comment">MiniUPnP daemon source code</td>
3087 <td></td>
3088</tr>
3089<tr>
3090 <td class="filename"><a href='download.php?file=miniupnpc-20070130.tar.gz'>miniupnpc-20070130.tar.gz</a></td>
3091 <td class="filesize">24466</td>
3092 <td class="filedate">29/01/2007 23:26:13 +0000</td>
3093 <td class="comment">MiniUPnP client source code</td>
3094 <td></td>
3095</tr>
3096<tr>
3097 <td class="filename"><a href='download.php?file=miniupnpd-20070127.tar.gz'>miniupnpd-20070127.tar.gz</a></td>
3098 <td class="filesize">42643</td>
3099 <td class="filedate">27/01/2007 16:02:35 +0000</td>
3100 <td class="comment">MiniUPnP daemon source code</td>
3101 <td></td>
3102</tr>
3103<tr>
3104 <td class="filename"><a href='download.php?file=miniupnpc-20070127.tar.gz'>miniupnpc-20070127.tar.gz</a></td>
3105 <td class="filesize">24241</td>
3106 <td class="filedate">27/01/2007 16:02:33 +0000</td>
3107 <td class="comment">MiniUPnP client source code</td>
3108 <td></td>
3109</tr>
3110<tr>
3111 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC2.tar.gz'>miniupnpd-1.0-RC2.tar.gz</a></td>
3112 <td class="filesize">40424</td>
3113 <td class="filedate">17/01/2007 16:13:05 +0000</td>
3114 <td class="comment">MiniUPnP daemon release source code</td>
3115 <td></td>
3116</tr>
3117<tr>
3118 <td class="filename"><a href='download.php?file=miniupnpd-20070112.tar.gz'>miniupnpd-20070112.tar.gz</a></td>
3119 <td class="filesize">40708</td>
3120 <td class="filedate">12/01/2007 13:40:50 +0000</td>
3121 <td class="comment">MiniUPnP daemon source code</td>
3122 <td></td>
3123</tr>
3124<tr>
3125 <td class="filename"><a href='download.php?file=miniupnpd-20070111.tar.gz'>miniupnpd-20070111.tar.gz</a></td>
3126 <td class="filesize">40651</td>
3127 <td class="filedate">11/01/2007 18:50:21 +0000</td>
3128 <td class="comment">MiniUPnP daemon source code</td>
3129 <td></td>
3130</tr>
3131<tr>
3132 <td class="filename"><a href='download.php?file=miniupnpd-20070108.tar.gz'>miniupnpd-20070108.tar.gz</a></td>
3133 <td class="filesize">40025</td>
3134 <td class="filedate">08/01/2007 10:02:14 +0000</td>
3135 <td class="comment">MiniUPnP daemon source code</td>
3136 <td></td>
3137</tr>
3138<tr>
3139 <td class="filename"><a href='download.php?file=miniupnpd-20070103.tar.gz'>miniupnpd-20070103.tar.gz</a></td>
3140 <td class="filesize">40065</td>
3141 <td class="filedate">03/01/2007 14:39:11 +0000</td>
3142 <td class="comment">MiniUPnP daemon source code</td>
3143 <td></td>
3144</tr>
3145<tr>
3146 <td class="filename"><a href='download.php?file=miniupnpc-20061214.tar.gz'>miniupnpc-20061214.tar.gz</a></td>
3147 <td class="filesize">24106</td>
3148 <td class="filedate">14/12/2006 15:43:54 +0000</td>
3149 <td class="comment">MiniUPnP client source code</td>
3150 <td></td>
3151</tr>
3152<tr>
3153 <td class="filename"><a href='download.php?file=miniupnpd-20061214.tar.gz'>miniupnpd-20061214.tar.gz</a></td>
3154 <td class="filesize">39750</td>
3155 <td class="filedate">14/12/2006 13:44:51 +0000</td>
3156 <td class="comment">MiniUPnP daemon source code</td>
3157 <td></td>
3158</tr>
3159<tr>
3160 <td class="filename"><a href='download.php?file=miniupnpd-1.0-RC1.tar.gz'>miniupnpd-1.0-RC1.tar.gz</a></td>
3161 <td class="filesize">39572</td>
3162 <td class="filedate">07/12/2006 10:55:31 +0000</td>
3163 <td class="comment">MiniUPnP daemon release source code</td>
3164 <td></td>
3165</tr>
3166<tr>
3167 <td class="filename"><a href='download.php?file=miniupnpc-1.0-RC1.tar.gz'>miniupnpc-1.0-RC1.tar.gz</a></td>
3168 <td class="filesize">23582</td>
3169 <td class="filedate">07/12/2006 10:55:30 +0000</td>
3170 <td class="comment">MiniUPnP client release source code</td>
3171 <td></td>
3172</tr>
3173<tr>
3174 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20061201.zip'>upnpc-exe-win32-20061201.zip</a></td>
3175 <td class="filesize">10378</td>
3176 <td class="filedate">01/12/2006 00:33:08 +0000</td>
3177 <td class="comment">Windows executable</td>
3178 <td></td>
3179</tr>
3180<tr>
3181 <td class="filename"><a href='download.php?file=miniupnpd20061130.tar.gz'>miniupnpd20061130.tar.gz</a></td>
3182 <td class="filesize">37184</td>
3183 <td class="filedate">30/11/2006 12:25:25 +0000</td>
3184 <td class="comment">MiniUPnP daemon source code</td>
3185 <td></td>
3186</tr>
3187<tr>
3188 <td class="filename"><a href='download.php?file=miniupnpd20061129.tar.gz'>miniupnpd20061129.tar.gz</a></td>
3189 <td class="filesize">36045</td>
3190 <td class="filedate">29/11/2006 00:10:49 +0000</td>
3191 <td class="comment">MiniUPnP daemon source code</td>
3192 <td></td>
3193</tr>
3194<tr>
3195 <td class="filename"><a href='download.php?file=miniupnpd20061127.tar.gz'>miniupnpd20061127.tar.gz</a></td>
3196 <td class="filesize">34155</td>
3197 <td class="filedate">26/11/2006 23:15:28 +0000</td>
3198 <td class="comment">MiniUPnP daemon source code</td>
3199 <td></td>
3200</tr>
3201<tr>
3202 <td class="filename"><a href='download.php?file=miniupnpc20061123.tar.gz'>miniupnpc20061123.tar.gz</a></td>
3203 <td class="filesize">21004</td>
3204 <td class="filedate">23/11/2006 22:41:46 +0000</td>
3205 <td class="comment">MiniUPnP client source code</td>
3206 <td></td>
3207</tr>
3208<tr>
3209 <td class="filename" colspan="2"><a href='download.php?file=miniupnpd-bin-openwrt20061123.tar.gz'>miniupnpd-bin-openwrt20061123.tar.gz</a></td>
3210 <td class="filedate">23/11/2006 22:41:44 +0000</td>
3211 <td class="comment">Precompiled binaries for openwrt</td>
3212 <td></td>
3213</tr>
3214<tr>
3215 <td class="filename"><a href='download.php?file=miniupnpd20061123.tar.gz'>miniupnpd20061123.tar.gz</a></td>
3216 <td class="filesize">33809</td>
3217 <td class="filedate">23/11/2006 22:28:29 +0000</td>
3218 <td class="comment">MiniUPnP daemon source code</td>
3219 <td></td>
3220</tr>
3221<tr>
3222 <td class="filename"><a href='download.php?file=miniupnpc20061119.tar.gz'>miniupnpc20061119.tar.gz</a></td>
3223 <td class="filesize">20897</td>
3224 <td class="filedate">19/11/2006 22:50:37 +0000</td>
3225 <td class="comment">MiniUPnP client source code</td>
3226 <td></td>
3227</tr>
3228<tr>
3229 <td class="filename"><a href='download.php?file=miniupnpd20061119.tar.gz'>miniupnpd20061119.tar.gz</a></td>
3230 <td class="filesize">32580</td>
3231 <td class="filedate">19/11/2006 22:50:36 +0000</td>
3232 <td class="comment">MiniUPnP daemon source code</td>
3233 <td></td>
3234</tr>
3235<tr>
3236 <td class="filename"><a href='download.php?file=miniupnpd20061117.tar.gz'>miniupnpd20061117.tar.gz</a></td>
3237 <td class="filesize">32646</td>
3238 <td class="filedate">17/11/2006 13:29:33 +0000</td>
3239 <td class="comment">MiniUPnP daemon source code</td>
3240 <td></td>
3241</tr>
3242<tr>
3243 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20061112.zip'>upnpc-exe-win32-20061112.zip</a></td>
3244 <td class="filesize">10262</td>
3245 <td class="filedate">12/11/2006 22:41:25 +0000</td>
3246 <td class="comment">Windows executable</td>
3247 <td></td>
3248</tr>
3249<tr>
3250 <td class="filename"><a href='download.php?file=miniupnpd20061112.tar.gz'>miniupnpd20061112.tar.gz</a></td>
3251 <td class="filesize">32023</td>
3252 <td class="filedate">12/11/2006 21:30:32 +0000</td>
3253 <td class="comment">MiniUPnP daemon source code</td>
3254 <td></td>
3255</tr>
3256<tr>
3257 <td class="filename"><a href='download.php?file=miniupnpc20061112.tar.gz'>miniupnpc20061112.tar.gz</a></td>
3258 <td class="filesize">21047</td>
3259 <td class="filedate">12/11/2006 21:30:31 +0000</td>
3260 <td class="comment">MiniUPnP client source code</td>
3261 <td></td>
3262</tr>
3263<tr>
3264 <td class="filename"><a href='download.php?file=miniupnpd20061110.tar.gz'>miniupnpd20061110.tar.gz</a></td>
3265 <td class="filesize">27926</td>
3266 <td class="filedate">09/11/2006 23:35:02 +0000</td>
3267 <td class="comment">MiniUPnP daemon source code</td>
3268 <td></td>
3269</tr>
3270<tr>
3271 <td class="filename"><a href='download.php?file=miniupnpc20061110.tar.gz'>miniupnpc20061110.tar.gz</a></td>
3272 <td class="filesize">21009</td>
3273 <td class="filedate">09/11/2006 23:32:19 +0000</td>
3274 <td class="comment">MiniUPnP client source code</td>
3275 <td></td>
3276</tr>
3277<tr>
3278 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20061101.zip'>upnpc-exe-win32-20061101.zip</a></td>
3279 <td class="filesize">10089</td>
3280 <td class="filedate">08/11/2006 20:35:09 +0000</td>
3281 <td class="comment">Windows executable</td>
3282 <td></td>
3283</tr>
3284<tr>
3285 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20061020.zip'>upnpc-exe-win32-20061020.zip</a></td>
3286 <td class="filesize">9183</td>
3287 <td class="filedate">08/11/2006 20:35:08 +0000</td>
3288 <td class="comment">Windows executable</td>
3289 <td></td>
3290</tr>
3291<tr>
3292 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20060909.zip'>upnpc-exe-win32-20060909.zip</a></td>
3293 <td class="filesize">9994</td>
3294 <td class="filedate">08/11/2006 20:35:07 +0000</td>
3295 <td class="comment">Windows executable</td>
3296 <td></td>
3297</tr>
3298<tr>
3299 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20060801.zip'>upnpc-exe-win32-20060801.zip</a></td>
3300 <td class="filesize">10002</td>
3301 <td class="filedate">08/11/2006 20:35:06 +0000</td>
3302 <td class="comment">Windows executable</td>
3303 <td></td>
3304</tr>
3305<tr>
3306 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20060711.zip'>upnpc-exe-win32-20060711.zip</a></td>
3307 <td class="filesize">13733</td>
3308 <td class="filedate">08/11/2006 20:35:05 +0000</td>
3309 <td class="comment">Windows executable</td>
3310 <td></td>
3311</tr>
3312<tr>
3313 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20060709.zip'>upnpc-exe-win32-20060709.zip</a></td>
3314 <td class="filesize">13713</td>
3315 <td class="filedate">08/11/2006 20:35:04 +0000</td>
3316 <td class="comment">Windows executable</td>
3317 <td></td>
3318</tr>
3319<tr>
3320 <td class="filename"><a href='download.php?file=upnpc-exe-win32-20060704.zip'>upnpc-exe-win32-20060704.zip</a></td>
3321 <td class="filesize">13297</td>
3322 <td class="filedate">08/11/2006 20:35:03 +0000</td>
3323 <td class="comment">Windows executable</td>
3324 <td></td>
3325</tr>
3326<tr>
3327 <td class="filename"><a href='download.php?file=miniupnpc20061107.tar.gz'>miniupnpc20061107.tar.gz</a></td>
3328 <td class="filesize">20708</td>
3329 <td class="filedate">06/11/2006 23:36:57 +0000</td>
3330 <td class="comment">MiniUPnP client source code</td>
3331 <td></td>
3332</tr>
3333<tr>
3334 <td class="filename"><a href='download.php?file=miniupnpd20061107.tar.gz'>miniupnpd20061107.tar.gz</a></td>
3335 <td class="filesize">26992</td>
3336 <td class="filedate">06/11/2006 23:35:06 +0000</td>
3337 <td class="comment">MiniUPnP daemon source code</td>
3338 <td></td>
3339</tr>
3340<tr>
3341 <td class="filename"><a href='download.php?file=miniupnpc20061106.tar.gz'>miniupnpc20061106.tar.gz</a></td>
3342 <td class="filesize">20575</td>
3343 <td class="filedate">06/11/2006 17:02:15 +0000</td>
3344 <td class="comment">MiniUPnP client source code</td>
3345 <td></td>
3346</tr>
3347<tr>
3348 <td class="filename"><a href='download.php?file=miniupnpd20061106.tar.gz'>miniupnpd20061106.tar.gz</a></td>
3349 <td class="filesize">26597</td>
3350 <td class="filedate">06/11/2006 15:39:10 +0000</td>
3351 <td class="comment">MiniUPnP daemon source code</td>
3352 <td></td>
3353</tr>
3354<tr>
3355 <td class="filename"><a href='download.php?file=miniupnpc20061101.tar.gz'>miniupnpc20061101.tar.gz</a></td>
3356 <td class="filesize">20395</td>
3357 <td class="filedate">04/11/2006 18:16:15 +0000</td>
3358 <td class="comment">MiniUPnP client source code</td>
3359 <td></td>
3360</tr>
3361<tr>
3362 <td class="filename"><a href='download.php?file=miniupnpc20061031.tar.gz'>miniupnpc20061031.tar.gz</a></td>
3363 <td class="filesize">20396</td>
3364 <td class="filedate">04/11/2006 18:16:13 +0000</td>
3365 <td class="comment">MiniUPnP client source code</td>
3366 <td></td>
3367</tr>
3368<tr>
3369 <td class="filename"><a href='download.php?file=miniupnpc20061023.tar.gz'>miniupnpc20061023.tar.gz</a></td>
3370 <td class="filesize">20109</td>
3371 <td class="filedate">04/11/2006 18:16:12 +0000</td>
3372 <td class="comment">MiniUPnP client source code</td>
3373 <td></td>
3374</tr>
3375<tr>
3376 <td class="filename"><a href='download.php?file=miniupnpc20061020.tar.gz'>miniupnpc20061020.tar.gz</a></td>
3377 <td class="filesize">19739</td>
3378 <td class="filedate">04/11/2006 18:16:10 +0000</td>
3379 <td class="comment">MiniUPnP client source code</td>
3380 <td></td>
3381</tr>
3382<tr>
3383 <td class="filename"><a href='download.php?file=miniupnpc20060909.tar.gz'>miniupnpc20060909.tar.gz</a></td>
3384 <td class="filesize">19285</td>
3385 <td class="filedate">04/11/2006 18:16:09 +0000</td>
3386 <td class="comment">MiniUPnP client source code</td>
3387 <td></td>
3388</tr>
3389<tr>
3390 <td class="filename"><a href='download.php?file=miniupnpc20060731.tar.gz'>miniupnpc20060731.tar.gz</a></td>
3391 <td class="filesize">19032</td>
3392 <td class="filedate">04/11/2006 18:16:07 +0000</td>
3393 <td class="comment">MiniUPnP client source code</td>
3394 <td></td>
3395</tr>
3396<tr>
3397 <td class="filename"><a href='download.php?file=miniupnpc20060711.tar.gz'>miniupnpc20060711.tar.gz</a></td>
3398 <td class="filesize">19151</td>
3399 <td class="filedate">04/11/2006 18:16:06 +0000</td>
3400 <td class="comment">MiniUPnP client source code</td>
3401 <td></td>
3402</tr>
3403<tr>
3404 <td class="filename"><a href='download.php?file=miniupnpc20060709.tar.gz'>miniupnpc20060709.tar.gz</a></td>
3405 <td class="filesize">19080</td>
3406 <td class="filedate">04/11/2006 18:16:04 +0000</td>
3407 <td class="comment">MiniUPnP client source code</td>
3408 <td></td>
3409</tr>
3410<tr>
3411 <td class="filename"><a href='download.php?file=miniupnpc20060703.tar.gz'>miniupnpc20060703.tar.gz</a></td>
3412 <td class="filesize">17906</td>
3413 <td class="filedate">04/11/2006 18:16:03 +0000</td>
3414 <td class="comment">MiniUPnP client source code</td>
3415 <td></td>
3416</tr>
3417<tr>
3418 <td class="filename"><a href='download.php?file=miniupnpc-new20060630.tar.gz'>miniupnpc-new20060630.tar.gz</a></td>
3419 <td class="filesize">14840</td>
3420 <td class="filedate">04/11/2006 18:16:01 +0000</td>
3421 <td class="comment">Jo&atilde;o Paulo Barraca version of the upnp client</td>
3422 <td></td>
3423</tr>
3424<tr>
3425 <td class="filename"><a href='download.php?file=miniupnpd20061029.tar.gz'>miniupnpd20061029.tar.gz</a></td>
3426 <td class="filesize">24197</td>
3427 <td class="filedate">03/11/2006 13:40:30 +0000</td>
3428 <td class="comment">MiniUPnP daemon source code</td>
3429 <td></td>
3430</tr>
3431<tr>
3432 <td class="filename"><a href='download.php?file=miniupnpd20061027.tar.gz'>miniupnpd20061027.tar.gz</a></td>
3433 <td class="filesize">23904</td>
3434 <td class="filedate">03/11/2006 13:40:29 +0000</td>
3435 <td class="comment">MiniUPnP daemon source code</td>
3436 <td></td>
3437</tr>
3438<tr>
3439 <td class="filename"><a href='download.php?file=miniupnpd20061028.tar.gz'>miniupnpd20061028.tar.gz</a></td>
3440 <td class="filesize">24383</td>
3441 <td class="filedate">03/11/2006 13:40:29 +0000</td>
3442 <td class="comment">MiniUPnP daemon source code</td>
3443 <td></td>
3444</tr>
3445<tr>
3446 <td class="filename"><a href='download.php?file=miniupnpd20061018.tar.gz'>miniupnpd20061018.tar.gz</a></td>
3447 <td class="filesize">23051</td>
3448 <td class="filedate">03/11/2006 13:40:28 +0000</td>
3449 <td class="comment">MiniUPnP daemon source code</td>
3450 <td></td>
3451</tr>
3452<tr>
3453 <td class="filename"><a href='download.php?file=miniupnpd20061023.tar.gz'>miniupnpd20061023.tar.gz</a></td>
3454 <td class="filesize">23478</td>
3455 <td class="filedate">03/11/2006 13:40:28 +0000</td>
3456 <td class="comment">MiniUPnP daemon source code</td>
3457 <td></td>
3458</tr>
3459<tr>
3460 <td class="filename"><a href='download.php?file=miniupnpd20060930.tar.gz'>miniupnpd20060930.tar.gz</a></td>
3461 <td class="filesize">22832</td>
3462 <td class="filedate">03/11/2006 13:40:28 +0000</td>
3463 <td class="comment">MiniUPnP daemon source code</td>
3464 <td></td>
3465</tr>
3466<tr>
3467 <td class="filename"><a href='download.php?file=miniupnpd20060924.tar.gz'>miniupnpd20060924.tar.gz</a></td>
3468 <td class="filesize">22038</td>
3469 <td class="filedate">03/11/2006 13:40:27 +0000</td>
3470 <td class="comment">MiniUPnP daemon source code</td>
3471 <td></td>
3472</tr>
3473<tr>
3474 <td class="filename"><a href='download.php?file=miniupnpd20060919.tar.gz'>miniupnpd20060919.tar.gz</a></td>
3475 <td class="filesize">21566</td>
3476 <td class="filedate">03/11/2006 13:40:27 +0000</td>
3477 <td class="comment">MiniUPnP daemon source code</td>
3478 <td></td>
3479</tr>
3480<tr>
3481 <td class="filename"><a href='download.php?file=miniupnpd20060729.tar.gz'>miniupnpd20060729.tar.gz</a></td>
3482 <td class="filesize">19202</td>
3483 <td class="filedate">03/11/2006 13:40:26 +0000</td>
3484 <td class="comment">MiniUPnP daemon source code</td>
3485 <td></td>
3486</tr>
3487<tr>
3488 <td class="filename"><a href='download.php?file=miniupnpd20060909.tar.gz'>miniupnpd20060909.tar.gz</a></td>
3489 <td class="filesize">19952</td>
3490 <td class="filedate">03/11/2006 13:40:26 +0000</td>
3491 <td class="comment">MiniUPnP daemon source code</td>
3492 <td></td>
3493</tr>
3494</table>
3495
3496<p><a href="..">Home</a></p>
3497<p>Contact: miniupnp _AT_ free _DOT_ fr</p>
3498<p align="center">
3499<a href="https://validator.w3.org/check?uri=referer"><img src="https://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0 Transitional" height="31" width="88" /></a>
3500<a href="https://jigsaw.w3.org/css-validator/check/referer"><img style="border:0;width:88px;height:31px" src="https://jigsaw.w3.org/css-validator/images/vcss" alt="Valid CSS!" /></a>
3501<!--
3502 <a href="https://freshmeat.net/projects/miniupnp"><img src="https://s3.amazonaws.com/entp-tender-production/assets/bc5be96f147ec8db3c10fc017f1f53889904ef5b/fm_logo_white_150_normal.png" border="0" alt="freshmeat.net" /></a>
3503-->
3504<!-- https://futuresimple.github.com/images/github_logo.png -->
3505<!-- <a href="https://github.com/miniupnp/miniupnp"><img src="https://assets-cdn.github.com/images/modules/logos_page/GitHub-Logo.png" alt="github.com" height="31" /></a> -->
3506<a href="https://github.com/miniupnp/miniupnp"><img style="position: absolute; top: 0; left: 0; border: 0;" src="https://github.blog/wp-content/uploads/2008/12/forkme_left_green_007200.png" alt="Fork me on GitHub" /></a>
3507</p>
3508
3509<script type="text/javascript">
3510var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
3511document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
3512</script>
3513<script type="text/javascript">
3514try {
3515 var ua = 'UA-10295521';
3516 if(window.location.hostname == 'miniupnp.free.fr')
3517 ua += '-1';
3518 else if(window.location.hostname == 'miniupnp.tuxfamily.org')
3519 ua += '-2';
3520 else ua = '';
3521 if(ua != '') {
3522 var pageTracker = _gat._getTracker(ua);
3523 pageTracker._trackPageview();
3524 }
3525} catch(err) {}</script>
3526</body>
3527</html>
3528
diff --git a/bitbake/lib/bb/tests/fetch.py b/bitbake/lib/bb/tests/fetch.py
index 7b2dac7b86..e027705bf4 100644
--- a/bitbake/lib/bb/tests/fetch.py
+++ b/bitbake/lib/bb/tests/fetch.py
@@ -6,21 +6,59 @@
6# SPDX-License-Identifier: GPL-2.0-only 6# SPDX-License-Identifier: GPL-2.0-only
7# 7#
8 8
9import contextlib
10import shutil
9import unittest 11import unittest
12import unittest.mock
13import urllib.parse
10import hashlib 14import hashlib
11import tempfile 15import tempfile
12import collections 16import collections
13import os 17import os
18import signal
19import tarfile
14from bb.fetch2 import URI 20from bb.fetch2 import URI
15from bb.fetch2 import FetchMethod 21from bb.fetch2 import FetchMethod
16import bb 22import bb
23import bb.utils
17from bb.tests.support.httpserver import HTTPService 24from bb.tests.support.httpserver import HTTPService
18 25
19def skipIfNoNetwork(): 26def skipIfNoNetwork():
20 if os.environ.get("BB_SKIP_NETTESTS") == "yes": 27 if os.environ.get("BB_SKIP_NETTESTS") == "yes":
21 return unittest.skip("Network tests being skipped") 28 return unittest.skip("network test")
22 return lambda f: f 29 return lambda f: f
23 30
31
32@contextlib.contextmanager
33def hide_directory(directory):
34 """Hide the given directory and restore it after the context is left"""
35 temp_name = directory + ".bak"
36 os.rename(directory, temp_name)
37 try:
38 yield
39 finally:
40 os.rename(temp_name, directory)
41
42
43class TestTimeout(Exception):
44 # Indicate to pytest that this is not a test suite
45 __test__ = False
46
47class Timeout():
48
49 def __init__(self, seconds):
50 self.seconds = seconds
51
52 def handle_timeout(self, signum, frame):
53 raise TestTimeout("Test failed: timeout reached")
54
55 def __enter__(self):
56 signal.signal(signal.SIGALRM, self.handle_timeout)
57 signal.alarm(self.seconds)
58
59 def __exit__(self, exc_type, exc_val, exc_tb):
60 signal.alarm(0)
61
24class URITest(unittest.TestCase): 62class URITest(unittest.TestCase):
25 test_uris = { 63 test_uris = {
26 "http://www.google.com/index.html" : { 64 "http://www.google.com/index.html" : {
@@ -286,6 +324,36 @@ class URITest(unittest.TestCase):
286 'params': {"someparam" : "1"}, 324 'params': {"someparam" : "1"},
287 'query': {}, 325 'query': {},
288 'relative': True 326 'relative': True
327 },
328 "https://www.innodisk.com/Download_file?9BE0BF6657;downloadfilename=EGPL-T101.zip": {
329 'uri': 'https://www.innodisk.com/Download_file?9BE0BF6657;downloadfilename=EGPL-T101.zip',
330 'scheme': 'https',
331 'hostname': 'www.innodisk.com',
332 'port': None,
333 'hostport': 'www.innodisk.com',
334 'path': '/Download_file',
335 'userinfo': '',
336 'userinfo': '',
337 'username': '',
338 'password': '',
339 'params': {"downloadfilename" : "EGPL-T101.zip"},
340 'query': {"9BE0BF6657": None},
341 'relative': False
342 },
343 "file://example@.service": {
344 'uri': 'file:example%40.service',
345 'scheme': 'file',
346 'hostname': '',
347 'port': None,
348 'hostport': '',
349 'path': 'example@.service',
350 'userinfo': '',
351 'userinfo': '',
352 'username': '',
353 'password': '',
354 'params': {},
355 'query': {},
356 'relative': True
289 } 357 }
290 358
291 } 359 }
@@ -376,7 +444,7 @@ class FetcherTest(unittest.TestCase):
376 def setUp(self): 444 def setUp(self):
377 self.origdir = os.getcwd() 445 self.origdir = os.getcwd()
378 self.d = bb.data.init() 446 self.d = bb.data.init()
379 self.tempdir = tempfile.mkdtemp() 447 self.tempdir = tempfile.mkdtemp(prefix="bitbake-fetch-")
380 self.dldir = os.path.join(self.tempdir, "download") 448 self.dldir = os.path.join(self.tempdir, "download")
381 os.mkdir(self.dldir) 449 os.mkdir(self.dldir)
382 self.d.setVar("DL_DIR", self.dldir) 450 self.d.setVar("DL_DIR", self.dldir)
@@ -390,63 +458,104 @@ class FetcherTest(unittest.TestCase):
390 if os.environ.get("BB_TMPDIR_NOCLEAN") == "yes": 458 if os.environ.get("BB_TMPDIR_NOCLEAN") == "yes":
391 print("Not cleaning up %s. Please remove manually." % self.tempdir) 459 print("Not cleaning up %s. Please remove manually." % self.tempdir)
392 else: 460 else:
461 bb.process.run('chmod u+rw -R %s' % self.tempdir)
393 bb.utils.prunedir(self.tempdir) 462 bb.utils.prunedir(self.tempdir)
394 463
464 def git(self, cmd, cwd=None):
465 if isinstance(cmd, str):
466 cmd = 'git -c safe.bareRepository=all ' + cmd
467 else:
468 cmd = ['git', '-c', 'safe.bareRepository=all'] + cmd
469 if cwd is None:
470 cwd = self.gitdir
471 return bb.process.run(cmd, cwd=cwd)[0]
472
473 def git_init(self, cwd=None):
474 self.git('init', cwd=cwd)
475 # Explicitly set initial branch to master as
476 # a common setup is to use other default
477 # branch than master.
478 self.git(['checkout', '-b', 'master'], cwd=cwd)
479
480 try:
481 self.git(['config', 'user.email'], cwd=cwd)
482 except bb.process.ExecutionError:
483 self.git(['config', 'user.email', 'you@example.com'], cwd=cwd)
484
485 try:
486 self.git(['config', 'user.name'], cwd=cwd)
487 except bb.process.ExecutionError:
488 self.git(['config', 'user.name', 'Your Name'], cwd=cwd)
489
395class MirrorUriTest(FetcherTest): 490class MirrorUriTest(FetcherTest):
396 491
397 replaceuris = { 492 replaceuris = {
398 ("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "http://somewhere.org/somedir/") 493 ("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", "git://.*/.*", "http://somewhere.org/somedir/")
399 : "http://somewhere.org/somedir/git2_git.invalid.infradead.org.mtd-utils.git.tar.gz", 494 : "http://somewhere.org/somedir/git2_git.invalid.infradead.org.mtd-utils.git.tar.gz",
400 ("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/somedir/\\2;protocol=http") 495 ("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/somedir/\\2;protocol=http")
401 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http", 496 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
402 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/somedir/\\2;protocol=http") 497 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/somedir/\\2;protocol=http")
403 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http", 498 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
404 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/\\2;protocol=http") 499 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", "git://.*/([^/]+/)*([^/]*)", "git://somewhere.org/\\2;protocol=http")
405 : "git://somewhere.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http", 500 : "git://somewhere.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
406 ("git://someserver.org/bitbake;tag=1234567890123456789012345678901234567890", "git://someserver.org/bitbake", "git://git.openembedded.org/bitbake") 501 ("git://someserver.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master", "git://someserver.org/bitbake", "git://git.openembedded.org/bitbake")
407 : "git://git.openembedded.org/bitbake;tag=1234567890123456789012345678901234567890", 502 : "git://git.openembedded.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master",
408 ("file://sstate-xyz.tgz", "file://.*", "file:///somewhere/1234/sstate-cache") 503 ("file://sstate-xyz.tgz", "file://.*", "file:///somewhere/1234/sstate-cache")
409 : "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz", 504 : "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz",
410 ("file://sstate-xyz.tgz", "file://.*", "file:///somewhere/1234/sstate-cache/") 505 ("file://sstate-xyz.tgz", "file://.*", "file:///somewhere/1234/sstate-cache/")
411 : "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz", 506 : "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz",
412 ("http://somewhere.org/somedir1/somedir2/somefile_1.2.3.tar.gz", "http://.*/.*", "http://somewhere2.org/somedir3") 507 ("http://somewhere.org/somedir1/somedir2/somefile_1.2.3.tar.gz", "http://.*/.*", "http://somewhere2.org/somedir3")
413 : "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz", 508 : "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz",
414 ("http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz") 509 ("http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz")
415 : "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz", 510 : "http://somewhere2.org/somedir3/somefile_1.2.3.tar.gz",
416 ("http://www.apache.org/dist/subversion/subversion-1.7.1.tar.bz2", "http://www.apache.org/dist", "http://archive.apache.org/dist") 511 ("http://www.apache.org/dist/subversion/subversion-1.7.1.tar.bz2", "http://www.apache.org/dist", "http://archive.apache.org/dist")
417 : "http://archive.apache.org/dist/subversion/subversion-1.7.1.tar.bz2", 512 : "http://archive.apache.org/dist/subversion/subversion-1.7.1.tar.bz2",
418 ("http://www.apache.org/dist/subversion/subversion-1.7.1.tar.bz2", "http://.*/.*", "file:///somepath/downloads/") 513 ("http://www.apache.org/dist/subversion/subversion-1.7.1.tar.bz2", "http://.*/.*", "file:///somepath/downloads/")
419 : "file:///somepath/downloads/subversion-1.7.1.tar.bz2", 514 : "file:///somepath/downloads/subversion-1.7.1.tar.bz2",
420 ("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "git://somewhere.org/somedir/BASENAME;protocol=http") 515 ("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", "git://.*/.*", "git://somewhere.org/somedir/BASENAME;protocol=http")
421 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http", 516 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
422 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "git://somewhere.org/somedir/BASENAME;protocol=http") 517 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", "git://.*/.*", "git://somewhere.org/somedir/BASENAME;protocol=http")
423 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http", 518 : "git://somewhere.org/somedir/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
424 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890", "git://.*/.*", "git://somewhere.org/somedir/MIRRORNAME;protocol=http") 519 ("git://git.invalid.infradead.org/foo/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", "git://.*/.*", "git://somewhere.org/somedir/MIRRORNAME;protocol=http")
425 : "git://somewhere.org/somedir/git.invalid.infradead.org.foo.mtd-utils.git;tag=1234567890123456789012345678901234567890;protocol=http", 520 : "git://somewhere.org/somedir/git.invalid.infradead.org.foo.mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
426 ("http://somewhere.org/somedir1/somedir2/somefile_1.2.3.tar.gz", "http://.*/.*", "http://somewhere2.org") 521 ("http://somewhere.org/somedir1/somedir2/somefile_1.2.3.tar.gz", "http://.*/.*", "http://somewhere2.org")
427 : "http://somewhere2.org/somefile_1.2.3.tar.gz", 522 : "http://somewhere2.org/somefile_1.2.3.tar.gz",
428 ("http://somewhere.org/somedir1/somedir2/somefile_1.2.3.tar.gz", "http://.*/.*", "http://somewhere2.org/") 523 ("http://somewhere.org/somedir1/somedir2/somefile_1.2.3.tar.gz", "http://.*/.*", "http://somewhere2.org/")
429 : "http://somewhere2.org/somefile_1.2.3.tar.gz", 524 : "http://somewhere2.org/somefile_1.2.3.tar.gz",
430 ("git://someserver.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master", "git://someserver.org/bitbake;branch=master", "git://git.openembedded.org/bitbake;protocol=http") 525 ("git://someserver.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master", "git://someserver.org/bitbake;branch=master", "git://git.openembedded.org/bitbake;protocol=http")
431 : "git://git.openembedded.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master;protocol=http", 526 : "git://git.openembedded.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
527 ("git://user1@someserver.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master", "git://someserver.org/bitbake;branch=master", "git://user2@git.openembedded.org/bitbake;protocol=http")
528 : "git://user2@git.openembedded.org/bitbake;tag=1234567890123456789012345678901234567890;branch=master;protocol=http",
529 ("git://someserver.org/bitbake;tag=1234567890123456789012345678901234567890;protocol=git;branch=master", "git://someserver.org/bitbake", "git://someotherserver.org/bitbake;protocol=https")
530 : "git://someotherserver.org/bitbake;tag=1234567890123456789012345678901234567890;protocol=https;branch=master",
531 ("gitsm://git.qemu.org/git/seabios.git/;protocol=https;name=roms/seabios;subpath=roms/seabios;bareclone=1;nobranch=1;rev=1234567890123456789012345678901234567890", "gitsm://.*/.*", "http://petalinux.xilinx.com/sswreleases/rel-v${XILINX_VER_MAIN}/downloads") : "http://petalinux.xilinx.com/sswreleases/rel-v%24%7BXILINX_VER_MAIN%7D/downloads/git2_git.qemu.org.git.seabios.git..tar.gz",
532 ("https://somewhere.org/example/1.0.0/example;downloadfilename=some-example-1.0.0.tgz", "https://.*/.*", "file:///mirror/PATH")
533 : "file:///mirror/example/1.0.0/some-example-1.0.0.tgz;downloadfilename=some-example-1.0.0.tgz",
534 ("https://somewhere.org/example-1.0.0.tgz;downloadfilename=some-example-1.0.0.tgz", "https://.*/.*", "file:///mirror/some-example-1.0.0.tgz")
535 : "file:///mirror/some-example-1.0.0.tgz;downloadfilename=some-example-1.0.0.tgz",
536 ("git://git.invalid.infradead.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", r"git://(?!internal\.git\.server).*/.*", "http://somewhere.org/somedir/")
537 : "http://somewhere.org/somedir/git2_git.invalid.infradead.org.mtd-utils.git.tar.gz",
538 ("git://internal.git.server.org/mtd-utils.git;tag=1234567890123456789012345678901234567890;branch=master", r"git://(?!internal\.git\.server).*/.*", "http://somewhere.org/somedir/")
539 : None,
432 540
433 #Renaming files doesn't work 541 #Renaming files doesn't work
434 #("http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere2.org/somedir3/somefile_2.3.4.tar.gz") : "http://somewhere2.org/somedir3/somefile_2.3.4.tar.gz" 542 #("http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere.org/somedir1/somefile_1.2.3.tar.gz", "http://somewhere2.org/somedir3/somefile_2.3.4.tar.gz") : "http://somewhere2.org/somedir3/somefile_2.3.4.tar.gz"
435 #("file://sstate-xyz.tgz", "file://.*/.*", "file:///somewhere/1234/sstate-cache") : "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz", 543 #("file://sstate-xyz.tgz", "file://.*/.*", "file:///somewhere/1234/sstate-cache") : "file:///somewhere/1234/sstate-cache/sstate-xyz.tgz",
436 } 544 }
437 545
438 mirrorvar = "http://.*/.* file:///somepath/downloads/ \n" \ 546 mirrorvar = "http://.*/.* file:///somepath/downloads/ " \
439 "git://someserver.org/bitbake git://git.openembedded.org/bitbake \n" \ 547 "git://someserver.org/bitbake git://git.openembedded.org/bitbake " \
440 "https://.*/.* file:///someotherpath/downloads/ \n" \ 548 "https?://.*/.* file:///someotherpath/downloads/ " \
441 "http://.*/.* file:///someotherpath/downloads/ \n" 549 "svn://svn.server1.com/ svn://svn.server2.com/"
442 550
443 def test_urireplace(self): 551 def test_urireplace(self):
552 self.d.setVar("FILESPATH", ".")
444 for k, v in self.replaceuris.items(): 553 for k, v in self.replaceuris.items():
445 ud = bb.fetch.FetchData(k[0], self.d) 554 ud = bb.fetch.FetchData(k[0], self.d)
446 ud.setup_localpath(self.d) 555 ud.setup_localpath(self.d)
447 mirrors = bb.fetch2.mirror_from_string("%s %s" % (k[1], k[2])) 556 mirrors = bb.fetch2.mirror_from_string("%s %s" % (k[1], k[2]))
448 newuris, uds = bb.fetch2.build_mirroruris(ud, mirrors, self.d) 557 newuris, uds = bb.fetch2.build_mirroruris(ud, mirrors, self.d)
449 self.assertEqual([v], newuris) 558 self.assertEqual([v] if v else [], newuris)
450 559
451 def test_urilist1(self): 560 def test_urilist1(self):
452 fetcher = bb.fetch.FetchData("http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d) 561 fetcher = bb.fetch.FetchData("http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d)
@@ -461,10 +570,17 @@ class MirrorUriTest(FetcherTest):
461 uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d) 570 uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d)
462 self.assertEqual(uris, ['file:///someotherpath/downloads/bitbake-1.0.tar.gz']) 571 self.assertEqual(uris, ['file:///someotherpath/downloads/bitbake-1.0.tar.gz'])
463 572
573 def test_urilistsvn(self):
574 # Catch svn:// -> svn:// bug
575 fetcher = bb.fetch.FetchData("svn://svn.server1.com/isource/svnroot/reponame/tags/tagname;module=path_in_tagnamefolder;protocol=https;rev=2", self.d)
576 mirrors = bb.fetch2.mirror_from_string(self.mirrorvar)
577 uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d)
578 self.assertEqual(uris, ['svn://svn.server2.com/isource/svnroot/reponame/tags/tagname;module=path_in_tagnamefolder;protocol=https;rev=2'])
579
464 def test_mirror_of_mirror(self): 580 def test_mirror_of_mirror(self):
465 # Test if mirror of a mirror works 581 # Test if mirror of a mirror works
466 mirrorvar = self.mirrorvar + " http://.*/.* http://otherdownloads.yoctoproject.org/downloads/ \n" 582 mirrorvar = self.mirrorvar + " http://.*/.* http://otherdownloads.yoctoproject.org/downloads/"
467 mirrorvar = mirrorvar + " http://otherdownloads.yoctoproject.org/.* http://downloads2.yoctoproject.org/downloads/ \n" 583 mirrorvar = mirrorvar + " http://otherdownloads.yoctoproject.org/.* http://downloads2.yoctoproject.org/downloads/"
468 fetcher = bb.fetch.FetchData("http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d) 584 fetcher = bb.fetch.FetchData("http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d)
469 mirrors = bb.fetch2.mirror_from_string(mirrorvar) 585 mirrors = bb.fetch2.mirror_from_string(mirrorvar)
470 uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d) 586 uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d)
@@ -473,30 +589,30 @@ class MirrorUriTest(FetcherTest):
473 'http://otherdownloads.yoctoproject.org/downloads/bitbake-1.0.tar.gz', 589 'http://otherdownloads.yoctoproject.org/downloads/bitbake-1.0.tar.gz',
474 'http://downloads2.yoctoproject.org/downloads/bitbake-1.0.tar.gz']) 590 'http://downloads2.yoctoproject.org/downloads/bitbake-1.0.tar.gz'])
475 591
476 recmirrorvar = "https://.*/[^/]* http://AAAA/A/A/A/ \n" \ 592 recmirrorvar = "https://.*/[^/]* http://aaaa/A/A/A/ " \
477 "https://.*/[^/]* https://BBBB/B/B/B/ \n" 593 "https://.*/[^/]* https://bbbb/B/B/B/"
478 594
479 def test_recursive(self): 595 def test_recursive(self):
480 fetcher = bb.fetch.FetchData("https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d) 596 fetcher = bb.fetch.FetchData("https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", self.d)
481 mirrors = bb.fetch2.mirror_from_string(self.recmirrorvar) 597 mirrors = bb.fetch2.mirror_from_string(self.recmirrorvar)
482 uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d) 598 uris, uds = bb.fetch2.build_mirroruris(fetcher, mirrors, self.d)
483 self.assertEqual(uris, ['http://AAAA/A/A/A/bitbake/bitbake-1.0.tar.gz', 599 self.assertEqual(uris, ['http://aaaa/A/A/A/bitbake/bitbake-1.0.tar.gz',
484 'https://BBBB/B/B/B/bitbake/bitbake-1.0.tar.gz', 600 'https://bbbb/B/B/B/bitbake/bitbake-1.0.tar.gz',
485 'http://AAAA/A/A/A/B/B/bitbake/bitbake-1.0.tar.gz']) 601 'http://aaaa/A/A/A/B/B/bitbake/bitbake-1.0.tar.gz'])
486 602
487 603
488class GitDownloadDirectoryNamingTest(FetcherTest): 604class GitDownloadDirectoryNamingTest(FetcherTest):
489 def setUp(self): 605 def setUp(self):
490 super(GitDownloadDirectoryNamingTest, self).setUp() 606 super(GitDownloadDirectoryNamingTest, self).setUp()
491 self.recipe_url = "git://git.openembedded.org/bitbake" 607 self.recipe_url = "git://git.openembedded.org/bitbake;branch=master;protocol=https"
492 self.recipe_dir = "git.openembedded.org.bitbake" 608 self.recipe_dir = "git.openembedded.org.bitbake"
493 self.mirror_url = "git://github.com/openembedded/bitbake.git" 609 self.mirror_url = "git://github.com/openembedded/bitbake.git;protocol=https;branch=master"
494 self.mirror_dir = "github.com.openembedded.bitbake.git" 610 self.mirror_dir = "github.com.openembedded.bitbake.git"
495 611
496 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40') 612 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40')
497 613
498 def setup_mirror_rewrite(self): 614 def setup_mirror_rewrite(self):
499 self.d.setVar("PREMIRRORS", self.recipe_url + " " + self.mirror_url + " \n") 615 self.d.setVar("PREMIRRORS", self.recipe_url + " " + self.mirror_url)
500 616
501 @skipIfNoNetwork() 617 @skipIfNoNetwork()
502 def test_that_directory_is_named_after_recipe_url_when_no_mirroring_is_used(self): 618 def test_that_directory_is_named_after_recipe_url_when_no_mirroring_is_used(self):
@@ -536,16 +652,16 @@ class GitDownloadDirectoryNamingTest(FetcherTest):
536class TarballNamingTest(FetcherTest): 652class TarballNamingTest(FetcherTest):
537 def setUp(self): 653 def setUp(self):
538 super(TarballNamingTest, self).setUp() 654 super(TarballNamingTest, self).setUp()
539 self.recipe_url = "git://git.openembedded.org/bitbake" 655 self.recipe_url = "git://git.openembedded.org/bitbake;branch=master;protocol=https"
540 self.recipe_tarball = "git2_git.openembedded.org.bitbake.tar.gz" 656 self.recipe_tarball = "git2_git.openembedded.org.bitbake.tar.gz"
541 self.mirror_url = "git://github.com/openembedded/bitbake.git" 657 self.mirror_url = "git://github.com/openembedded/bitbake.git;protocol=https;branch=master"
542 self.mirror_tarball = "git2_github.com.openembedded.bitbake.git.tar.gz" 658 self.mirror_tarball = "git2_github.com.openembedded.bitbake.git.tar.gz"
543 659
544 self.d.setVar('BB_GENERATE_MIRROR_TARBALLS', '1') 660 self.d.setVar('BB_GENERATE_MIRROR_TARBALLS', '1')
545 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40') 661 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40')
546 662
547 def setup_mirror_rewrite(self): 663 def setup_mirror_rewrite(self):
548 self.d.setVar("PREMIRRORS", self.recipe_url + " " + self.mirror_url + " \n") 664 self.d.setVar("PREMIRRORS", self.recipe_url + " " + self.mirror_url)
549 665
550 @skipIfNoNetwork() 666 @skipIfNoNetwork()
551 def test_that_the_recipe_tarball_is_created_when_no_mirroring_is_used(self): 667 def test_that_the_recipe_tarball_is_created_when_no_mirroring_is_used(self):
@@ -570,9 +686,9 @@ class TarballNamingTest(FetcherTest):
570class GitShallowTarballNamingTest(FetcherTest): 686class GitShallowTarballNamingTest(FetcherTest):
571 def setUp(self): 687 def setUp(self):
572 super(GitShallowTarballNamingTest, self).setUp() 688 super(GitShallowTarballNamingTest, self).setUp()
573 self.recipe_url = "git://git.openembedded.org/bitbake" 689 self.recipe_url = "git://git.openembedded.org/bitbake;branch=master;protocol=https"
574 self.recipe_tarball = "gitshallow_git.openembedded.org.bitbake_82ea737-1_master.tar.gz" 690 self.recipe_tarball = "gitshallow_git.openembedded.org.bitbake_82ea737-1_master.tar.gz"
575 self.mirror_url = "git://github.com/openembedded/bitbake.git" 691 self.mirror_url = "git://github.com/openembedded/bitbake.git;protocol=https;branch=master"
576 self.mirror_tarball = "gitshallow_github.com.openembedded.bitbake.git_82ea737-1_master.tar.gz" 692 self.mirror_tarball = "gitshallow_github.com.openembedded.bitbake.git_82ea737-1_master.tar.gz"
577 693
578 self.d.setVar('BB_GIT_SHALLOW', '1') 694 self.d.setVar('BB_GIT_SHALLOW', '1')
@@ -580,7 +696,7 @@ class GitShallowTarballNamingTest(FetcherTest):
580 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40') 696 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40')
581 697
582 def setup_mirror_rewrite(self): 698 def setup_mirror_rewrite(self):
583 self.d.setVar("PREMIRRORS", self.recipe_url + " " + self.mirror_url + " \n") 699 self.d.setVar("PREMIRRORS", self.recipe_url + " " + self.mirror_url)
584 700
585 @skipIfNoNetwork() 701 @skipIfNoNetwork()
586 def test_that_the_tarball_is_named_after_recipe_url_when_no_mirroring_is_used(self): 702 def test_that_the_tarball_is_named_after_recipe_url_when_no_mirroring_is_used(self):
@@ -602,6 +718,39 @@ class GitShallowTarballNamingTest(FetcherTest):
602 self.assertIn(self.mirror_tarball, dir) 718 self.assertIn(self.mirror_tarball, dir)
603 719
604 720
721class CleanTarballTest(FetcherTest):
722 def setUp(self):
723 super(CleanTarballTest, self).setUp()
724 self.recipe_url = "git://git.openembedded.org/bitbake;protocol=https;branch=master"
725 self.recipe_tarball = "git2_git.openembedded.org.bitbake.tar.gz"
726
727 self.d.setVar('BB_GENERATE_MIRROR_TARBALLS', '1')
728 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40')
729
730 @skipIfNoNetwork()
731 def test_that_the_tarball_contents_does_not_leak_info(self):
732 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
733
734 fetcher.download()
735
736 fetcher.unpack(self.unpackdir)
737 mtime = bb.process.run('git log --all -1 --format=%ct',
738 cwd=os.path.join(self.unpackdir, 'git'))
739 self.assertEqual(len(mtime), 2)
740 mtime = int(mtime[0])
741
742 archive = tarfile.open(os.path.join(self.dldir, self.recipe_tarball))
743 self.assertNotEqual(len(archive.members), 0)
744 for member in archive.members:
745 if member.name == ".":
746 continue
747 self.assertEqual(member.uname, 'oe', "user name for %s differs" % member.name)
748 self.assertEqual(member.uid, 0, "uid for %s differs" % member.name)
749 self.assertEqual(member.gname, 'oe', "group name for %s differs" % member.name)
750 self.assertEqual(member.gid, 0, "gid for %s differs" % member.name)
751 self.assertEqual(member.mtime, mtime, "mtime for %s differs" % member.name)
752
753
605class FetcherLocalTest(FetcherTest): 754class FetcherLocalTest(FetcherTest):
606 def setUp(self): 755 def setUp(self):
607 def touch(fn): 756 def touch(fn):
@@ -613,12 +762,16 @@ class FetcherLocalTest(FetcherTest):
613 os.makedirs(self.localsrcdir) 762 os.makedirs(self.localsrcdir)
614 touch(os.path.join(self.localsrcdir, 'a')) 763 touch(os.path.join(self.localsrcdir, 'a'))
615 touch(os.path.join(self.localsrcdir, 'b')) 764 touch(os.path.join(self.localsrcdir, 'b'))
765 touch(os.path.join(self.localsrcdir, 'c@d'))
616 os.makedirs(os.path.join(self.localsrcdir, 'dir')) 766 os.makedirs(os.path.join(self.localsrcdir, 'dir'))
617 touch(os.path.join(self.localsrcdir, 'dir', 'c')) 767 touch(os.path.join(self.localsrcdir, 'dir', 'c'))
618 touch(os.path.join(self.localsrcdir, 'dir', 'd')) 768 touch(os.path.join(self.localsrcdir, 'dir', 'd'))
619 os.makedirs(os.path.join(self.localsrcdir, 'dir', 'subdir')) 769 os.makedirs(os.path.join(self.localsrcdir, 'dir', 'subdir'))
620 touch(os.path.join(self.localsrcdir, 'dir', 'subdir', 'e')) 770 touch(os.path.join(self.localsrcdir, 'dir', 'subdir', 'e'))
621 touch(os.path.join(self.localsrcdir, r'backslash\x2dsystemd-unit.device')) 771 touch(os.path.join(self.localsrcdir, r'backslash\x2dsystemd-unit.device'))
772 bb.process.run('tar cf archive.tar -C dir .', cwd=self.localsrcdir)
773 bb.process.run('tar czf archive.tar.gz -C dir .', cwd=self.localsrcdir)
774 bb.process.run('tar cjf archive.tar.bz2 -C dir .', cwd=self.localsrcdir)
622 self.d.setVar("FILESPATH", self.localsrcdir) 775 self.d.setVar("FILESPATH", self.localsrcdir)
623 776
624 def fetchUnpack(self, uris): 777 def fetchUnpack(self, uris):
@@ -632,10 +785,19 @@ class FetcherLocalTest(FetcherTest):
632 flst.sort() 785 flst.sort()
633 return flst 786 return flst
634 787
788 def test_local_checksum_fails_no_file(self):
789 self.d.setVar("SRC_URI", "file://404")
790 with self.assertRaises(bb.BBHandledException):
791 bb.fetch.get_checksum_file_list(self.d)
792
635 def test_local(self): 793 def test_local(self):
636 tree = self.fetchUnpack(['file://a', 'file://dir/c']) 794 tree = self.fetchUnpack(['file://a', 'file://dir/c'])
637 self.assertEqual(tree, ['a', 'dir/c']) 795 self.assertEqual(tree, ['a', 'dir/c'])
638 796
797 def test_local_at(self):
798 tree = self.fetchUnpack(['file://c@d'])
799 self.assertEqual(tree, ['c@d'])
800
639 def test_local_backslash(self): 801 def test_local_backslash(self):
640 tree = self.fetchUnpack([r'file://backslash\x2dsystemd-unit.device']) 802 tree = self.fetchUnpack([r'file://backslash\x2dsystemd-unit.device'])
641 self.assertEqual(tree, [r'backslash\x2dsystemd-unit.device']) 803 self.assertEqual(tree, [r'backslash\x2dsystemd-unit.device'])
@@ -673,57 +835,58 @@ class FetcherLocalTest(FetcherTest):
673 with self.assertRaises(bb.fetch2.UnpackError): 835 with self.assertRaises(bb.fetch2.UnpackError):
674 self.fetchUnpack(['file://a;subdir=/bin/sh']) 836 self.fetchUnpack(['file://a;subdir=/bin/sh'])
675 837
676 def test_local_gitfetch_usehead(self): 838 def test_local_striplevel(self):
839 tree = self.fetchUnpack(['file://archive.tar;subdir=bar;striplevel=1'])
840 self.assertEqual(tree, ['bar/c', 'bar/d', 'bar/subdir/e'])
841
842 def test_local_striplevel_gzip(self):
843 tree = self.fetchUnpack(['file://archive.tar.gz;subdir=bar;striplevel=1'])
844 self.assertEqual(tree, ['bar/c', 'bar/d', 'bar/subdir/e'])
845
846 def test_local_striplevel_bzip2(self):
847 tree = self.fetchUnpack(['file://archive.tar.bz2;subdir=bar;striplevel=1'])
848 self.assertEqual(tree, ['bar/c', 'bar/d', 'bar/subdir/e'])
849
850 def dummyGitTest(self, suffix):
677 # Create dummy local Git repo 851 # Create dummy local Git repo
678 src_dir = tempfile.mkdtemp(dir=self.tempdir, 852 src_dir = tempfile.mkdtemp(dir=self.tempdir,
679 prefix='gitfetch_localusehead_') 853 prefix='gitfetch_localusehead_')
680 src_dir = os.path.abspath(src_dir) 854 self.gitdir = os.path.abspath(src_dir)
681 bb.process.run("git init", cwd=src_dir) 855 self.git_init()
682 bb.process.run("git commit --allow-empty -m'Dummy commit'", 856 self.git(['commit', '--allow-empty', '-m', 'Dummy commit'])
683 cwd=src_dir)
684 # Use other branch than master 857 # Use other branch than master
685 bb.process.run("git checkout -b my-devel", cwd=src_dir) 858 self.git(['checkout', '-b', 'my-devel'])
686 bb.process.run("git commit --allow-empty -m'Dummy commit 2'", 859 self.git(['commit', '--allow-empty', '-m', 'Dummy commit 2'])
687 cwd=src_dir) 860 orig_rev = self.git(['rev-parse', 'HEAD']).strip()
688 stdout = bb.process.run("git rev-parse HEAD", cwd=src_dir)
689 orig_rev = stdout[0].strip()
690 861
691 # Fetch and check revision 862 # Fetch and check revision
692 self.d.setVar("SRCREV", "AUTOINC") 863 self.d.setVar("SRCREV", "AUTOINC")
693 url = "git://" + src_dir + ";protocol=file;usehead=1" 864 self.d.setVar("__BBSRCREV_SEEN", "1")
865 url = "git://" + self.gitdir + ";branch=master;protocol=file;" + suffix
694 fetcher = bb.fetch.Fetch([url], self.d) 866 fetcher = bb.fetch.Fetch([url], self.d)
695 fetcher.download() 867 fetcher.download()
696 fetcher.unpack(self.unpackdir) 868 fetcher.unpack(self.unpackdir)
697 stdout = bb.process.run("git rev-parse HEAD", 869 unpack_rev = self.git(['rev-parse', 'HEAD'],
698 cwd=os.path.join(self.unpackdir, 'git')) 870 cwd=os.path.join(self.unpackdir, 'git')).strip()
699 unpack_rev = stdout[0].strip()
700 self.assertEqual(orig_rev, unpack_rev) 871 self.assertEqual(orig_rev, unpack_rev)
701 872
873 def test_local_gitfetch_usehead(self):
874 self.dummyGitTest("usehead=1")
875
702 def test_local_gitfetch_usehead_withname(self): 876 def test_local_gitfetch_usehead_withname(self):
703 # Create dummy local Git repo 877 self.dummyGitTest("usehead=1;name=newName")
704 src_dir = tempfile.mkdtemp(dir=self.tempdir,
705 prefix='gitfetch_localusehead_')
706 src_dir = os.path.abspath(src_dir)
707 bb.process.run("git init", cwd=src_dir)
708 bb.process.run("git commit --allow-empty -m'Dummy commit'",
709 cwd=src_dir)
710 # Use other branch than master
711 bb.process.run("git checkout -b my-devel", cwd=src_dir)
712 bb.process.run("git commit --allow-empty -m'Dummy commit 2'",
713 cwd=src_dir)
714 stdout = bb.process.run("git rev-parse HEAD", cwd=src_dir)
715 orig_rev = stdout[0].strip()
716 878
717 # Fetch and check revision 879 def test_local_gitfetch_shared(self):
718 self.d.setVar("SRCREV", "AUTOINC") 880 self.dummyGitTest("usehead=1;name=sharedName")
719 url = "git://" + src_dir + ";protocol=file;usehead=1;name=newName" 881 alt = os.path.join(self.unpackdir, 'git/.git/objects/info/alternates')
720 fetcher = bb.fetch.Fetch([url], self.d) 882 self.assertTrue(os.path.exists(alt))
721 fetcher.download() 883
722 fetcher.unpack(self.unpackdir) 884 def test_local_gitfetch_noshared(self):
723 stdout = bb.process.run("git rev-parse HEAD", 885 self.d.setVar('BB_GIT_NOSHARED', '1')
724 cwd=os.path.join(self.unpackdir, 'git')) 886 self.unpackdir += '_noshared'
725 unpack_rev = stdout[0].strip() 887 self.dummyGitTest("usehead=1;name=noSharedName")
726 self.assertEqual(orig_rev, unpack_rev) 888 alt = os.path.join(self.unpackdir, 'git/.git/objects/info/alternates')
889 self.assertFalse(os.path.exists(alt))
727 890
728class FetcherNoNetworkTest(FetcherTest): 891class FetcherNoNetworkTest(FetcherTest):
729 def setUp(self): 892 def setUp(self):
@@ -831,12 +994,12 @@ class FetcherNoNetworkTest(FetcherTest):
831class FetcherNetworkTest(FetcherTest): 994class FetcherNetworkTest(FetcherTest):
832 @skipIfNoNetwork() 995 @skipIfNoNetwork()
833 def test_fetch(self): 996 def test_fetch(self):
834 fetcher = bb.fetch.Fetch(["http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d) 997 fetcher = bb.fetch.Fetch(["https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d)
835 fetcher.download() 998 fetcher.download()
836 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749) 999 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
837 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.1.tar.gz"), 57892) 1000 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.1.tar.gz"), 57892)
838 self.d.setVar("BB_NO_NETWORK", "1") 1001 self.d.setVar("BB_NO_NETWORK", "1")
839 fetcher = bb.fetch.Fetch(["http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "http://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d) 1002 fetcher = bb.fetch.Fetch(["https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz", "https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz"], self.d)
840 fetcher.download() 1003 fetcher.download()
841 fetcher.unpack(self.unpackdir) 1004 fetcher.unpack(self.unpackdir)
842 self.assertEqual(len(os.listdir(self.unpackdir + "/bitbake-1.0/")), 9) 1005 self.assertEqual(len(os.listdir(self.unpackdir + "/bitbake-1.0/")), 9)
@@ -844,21 +1007,22 @@ class FetcherNetworkTest(FetcherTest):
844 1007
845 @skipIfNoNetwork() 1008 @skipIfNoNetwork()
846 def test_fetch_mirror(self): 1009 def test_fetch_mirror(self):
847 self.d.setVar("MIRRORS", "http://.*/.* http://downloads.yoctoproject.org/releases/bitbake") 1010 self.d.setVar("MIRRORS", "http://.*/.* https://downloads.yoctoproject.org/releases/bitbake")
848 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d) 1011 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
849 fetcher.download() 1012 fetcher.download()
850 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749) 1013 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
851 1014
852 @skipIfNoNetwork() 1015 @skipIfNoNetwork()
853 def test_fetch_mirror_of_mirror(self): 1016 def test_fetch_mirror_of_mirror(self):
854 self.d.setVar("MIRRORS", "http://.*/.* http://invalid2.yoctoproject.org/ \n http://invalid2.yoctoproject.org/.* http://downloads.yoctoproject.org/releases/bitbake") 1017 self.d.setVar("MIRRORS", "http://.*/.* http://invalid2.yoctoproject.org/ http://invalid2.yoctoproject.org/.* https://downloads.yoctoproject.org/releases/bitbake")
855 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d) 1018 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
856 fetcher.download() 1019 fetcher.download()
857 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749) 1020 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
858 1021
859 @skipIfNoNetwork() 1022 @skipIfNoNetwork()
860 def test_fetch_file_mirror_of_mirror(self): 1023 def test_fetch_file_mirror_of_mirror(self):
861 self.d.setVar("MIRRORS", "http://.*/.* file:///some1where/ \n file:///some1where/.* file://some2where/ \n file://some2where/.* http://downloads.yoctoproject.org/releases/bitbake") 1024 self.d.setVar("FILESPATH", ".")
1025 self.d.setVar("MIRRORS", "http://.*/.* file:///some1where/ file:///some1where/.* file://some2where/ file://some2where/.* https://downloads.yoctoproject.org/releases/bitbake")
862 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d) 1026 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
863 os.mkdir(self.dldir + "/some2where") 1027 os.mkdir(self.dldir + "/some2where")
864 fetcher.download() 1028 fetcher.download()
@@ -866,16 +1030,46 @@ class FetcherNetworkTest(FetcherTest):
866 1030
867 @skipIfNoNetwork() 1031 @skipIfNoNetwork()
868 def test_fetch_premirror(self): 1032 def test_fetch_premirror(self):
869 self.d.setVar("PREMIRRORS", "http://.*/.* http://downloads.yoctoproject.org/releases/bitbake") 1033 self.d.setVar("PREMIRRORS", "http://.*/.* https://downloads.yoctoproject.org/releases/bitbake")
870 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d) 1034 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz"], self.d)
871 fetcher.download() 1035 fetcher.download()
872 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749) 1036 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
873 1037
874 @skipIfNoNetwork() 1038 @skipIfNoNetwork()
1039 def test_fetch_specify_downloadfilename(self):
1040 fetcher = bb.fetch.Fetch(["https://downloads.yoctoproject.org/releases/bitbake/bitbake-1.0.tar.gz;downloadfilename=bitbake-v1.0.0.tar.gz"], self.d)
1041 fetcher.download()
1042 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-v1.0.0.tar.gz"), 57749)
1043
1044 @skipIfNoNetwork()
1045 def test_fetch_premirror_specify_downloadfilename_regex_uri(self):
1046 self.d.setVar("PREMIRRORS", "http://.*/.* https://downloads.yoctoproject.org/releases/bitbake/")
1047 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/1.0.tar.gz;downloadfilename=bitbake-1.0.tar.gz"], self.d)
1048 fetcher.download()
1049 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
1050
1051 @skipIfNoNetwork()
1052 # BZ13039
1053 def test_fetch_premirror_specify_downloadfilename_specific_uri(self):
1054 self.d.setVar("PREMIRRORS", "http://invalid.yoctoproject.org/releases/bitbake https://downloads.yoctoproject.org/releases/bitbake")
1055 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/1.0.tar.gz;downloadfilename=bitbake-1.0.tar.gz"], self.d)
1056 fetcher.download()
1057 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
1058
1059 @skipIfNoNetwork()
1060 def test_fetch_premirror_use_downloadfilename_to_fetch(self):
1061 # Ensure downloadfilename is used when fetching from premirror.
1062 self.d.setVar("PREMIRRORS", "http://.*/.* https://downloads.yoctoproject.org/releases/bitbake")
1063 fetcher = bb.fetch.Fetch(["http://invalid.yoctoproject.org/releases/bitbake/bitbake-1.1.tar.gz;downloadfilename=bitbake-1.0.tar.gz"], self.d)
1064 fetcher.download()
1065 self.assertEqual(os.path.getsize(self.dldir + "/bitbake-1.0.tar.gz"), 57749)
1066
1067 @skipIfNoNetwork()
875 def gitfetcher(self, url1, url2): 1068 def gitfetcher(self, url1, url2):
876 def checkrevision(self, fetcher): 1069 def checkrevision(self, fetcher):
877 fetcher.unpack(self.unpackdir) 1070 fetcher.unpack(self.unpackdir)
878 revision = bb.process.run("git rev-parse HEAD", shell=True, cwd=self.unpackdir + "/git")[0].strip() 1071 revision = self.git(['rev-parse', 'HEAD'],
1072 cwd=os.path.join(self.unpackdir, 'git')).strip()
879 self.assertEqual(revision, "270a05b0b4ba0959fe0624d2a4885d7b70426da5") 1073 self.assertEqual(revision, "270a05b0b4ba0959fe0624d2a4885d7b70426da5")
880 1074
881 self.d.setVar("BB_GENERATE_MIRROR_TARBALLS", "1") 1075 self.d.setVar("BB_GENERATE_MIRROR_TARBALLS", "1")
@@ -893,25 +1087,19 @@ class FetcherNetworkTest(FetcherTest):
893 1087
894 @skipIfNoNetwork() 1088 @skipIfNoNetwork()
895 def test_gitfetch(self): 1089 def test_gitfetch(self):
896 url1 = url2 = "git://git.openembedded.org/bitbake" 1090 url1 = url2 = "git://git.openembedded.org/bitbake;branch=master;protocol=https"
897 self.gitfetcher(url1, url2) 1091 self.gitfetcher(url1, url2)
898 1092
899 @skipIfNoNetwork() 1093 @skipIfNoNetwork()
900 def test_gitfetch_goodsrcrev(self): 1094 def test_gitfetch_goodsrcrev(self):
901 # SRCREV is set but matches rev= parameter 1095 # SRCREV is set but matches rev= parameter
902 url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5" 1096 url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5;branch=master;protocol=https"
903 self.gitfetcher(url1, url2) 1097 self.gitfetcher(url1, url2)
904 1098
905 @skipIfNoNetwork() 1099 @skipIfNoNetwork()
906 def test_gitfetch_badsrcrev(self): 1100 def test_gitfetch_badsrcrev(self):
907 # SRCREV is set but does not match rev= parameter 1101 # SRCREV is set but does not match rev= parameter
908 url1 = url2 = "git://git.openembedded.org/bitbake;rev=dead05b0b4ba0959fe0624d2a4885d7b70426da5" 1102 url1 = url2 = "git://git.openembedded.org/bitbake;rev=dead05b0b4ba0959fe0624d2a4885d7b70426da5;branch=master;protocol=https"
909 self.assertRaises(bb.fetch.FetchError, self.gitfetcher, url1, url2)
910
911 @skipIfNoNetwork()
912 def test_gitfetch_tagandrev(self):
913 # SRCREV is set but does not match rev= parameter
914 url1 = url2 = "git://git.openembedded.org/bitbake;rev=270a05b0b4ba0959fe0624d2a4885d7b70426da5;tag=270a05b0b4ba0959fe0624d2a4885d7b70426da5"
915 self.assertRaises(bb.fetch.FetchError, self.gitfetcher, url1, url2) 1103 self.assertRaises(bb.fetch.FetchError, self.gitfetcher, url1, url2)
916 1104
917 @skipIfNoNetwork() 1105 @skipIfNoNetwork()
@@ -920,7 +1108,7 @@ class FetcherNetworkTest(FetcherTest):
920 # `usehead=1' and instead fetch the specified SRCREV. See 1108 # `usehead=1' and instead fetch the specified SRCREV. See
921 # test_local_gitfetch_usehead() for a positive use of the usehead 1109 # test_local_gitfetch_usehead() for a positive use of the usehead
922 # feature. 1110 # feature.
923 url = "git://git.openembedded.org/bitbake;usehead=1" 1111 url = "git://git.openembedded.org/bitbake;usehead=1;branch=master;protocol=https"
924 self.assertRaises(bb.fetch.ParameterError, self.gitfetcher, url, url) 1112 self.assertRaises(bb.fetch.ParameterError, self.gitfetcher, url, url)
925 1113
926 @skipIfNoNetwork() 1114 @skipIfNoNetwork()
@@ -929,38 +1117,38 @@ class FetcherNetworkTest(FetcherTest):
929 # `usehead=1' and instead fetch the specified SRCREV. See 1117 # `usehead=1' and instead fetch the specified SRCREV. See
930 # test_local_gitfetch_usehead() for a positive use of the usehead 1118 # test_local_gitfetch_usehead() for a positive use of the usehead
931 # feature. 1119 # feature.
932 url = "git://git.openembedded.org/bitbake;usehead=1;name=newName" 1120 url = "git://git.openembedded.org/bitbake;usehead=1;name=newName;branch=master;protocol=https"
933 self.assertRaises(bb.fetch.ParameterError, self.gitfetcher, url, url) 1121 self.assertRaises(bb.fetch.ParameterError, self.gitfetcher, url, url)
934 1122
935 @skipIfNoNetwork() 1123 @skipIfNoNetwork()
936 def test_gitfetch_finds_local_tarball_for_mirrored_url_when_previous_downloaded_by_the_recipe_url(self): 1124 def test_gitfetch_finds_local_tarball_for_mirrored_url_when_previous_downloaded_by_the_recipe_url(self):
937 recipeurl = "git://git.openembedded.org/bitbake" 1125 recipeurl = "git://git.openembedded.org/bitbake;branch=master;protocol=https"
938 mirrorurl = "git://someserver.org/bitbake" 1126 mirrorurl = "git://someserver.org/bitbake;branch=master;protocol=https"
939 self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake \n") 1127 self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake")
940 self.gitfetcher(recipeurl, mirrorurl) 1128 self.gitfetcher(recipeurl, mirrorurl)
941 1129
942 @skipIfNoNetwork() 1130 @skipIfNoNetwork()
943 def test_gitfetch_finds_local_tarball_when_previous_downloaded_from_a_premirror(self): 1131 def test_gitfetch_finds_local_tarball_when_previous_downloaded_from_a_premirror(self):
944 recipeurl = "git://someserver.org/bitbake" 1132 recipeurl = "git://someserver.org/bitbake;branch=master;protocol=https"
945 self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake \n") 1133 self.d.setVar("PREMIRRORS", "git://someserver.org/bitbake git://git.openembedded.org/bitbake")
946 self.gitfetcher(recipeurl, recipeurl) 1134 self.gitfetcher(recipeurl, recipeurl)
947 1135
948 @skipIfNoNetwork() 1136 @skipIfNoNetwork()
949 def test_gitfetch_finds_local_repository_when_premirror_rewrites_the_recipe_url(self): 1137 def test_gitfetch_finds_local_repository_when_premirror_rewrites_the_recipe_url(self):
950 realurl = "git://git.openembedded.org/bitbake" 1138 realurl = "https://git.openembedded.org/bitbake"
951 recipeurl = "git://someserver.org/bitbake" 1139 recipeurl = "git://someserver.org/bitbake;protocol=https;branch=master"
952 self.sourcedir = self.unpackdir.replace("unpacked", "sourcemirror.git") 1140 self.sourcedir = self.unpackdir.replace("unpacked", "sourcemirror.git")
953 os.chdir(self.tempdir) 1141 os.chdir(self.tempdir)
954 bb.process.run("git clone %s %s 2> /dev/null" % (realurl, self.sourcedir), shell=True) 1142 self.git(['clone', realurl, self.sourcedir], cwd=self.tempdir)
955 self.d.setVar("PREMIRRORS", "%s git://%s;protocol=file \n" % (recipeurl, self.sourcedir)) 1143 self.d.setVar("PREMIRRORS", "%s git://%s;protocol=file" % (recipeurl, self.sourcedir))
956 self.gitfetcher(recipeurl, recipeurl) 1144 self.gitfetcher(recipeurl, recipeurl)
957 1145
958 @skipIfNoNetwork() 1146 @skipIfNoNetwork()
959 def test_git_submodule(self): 1147 def test_git_submodule(self):
960 # URL with ssh submodules 1148 # URL with ssh submodules
961 url = "gitsm://git.yoctoproject.org/git-submodule-test;branch=ssh-gitsm-tests;rev=049da4a6cb198d7c0302e9e8b243a1443cb809a7" 1149 url = "gitsm://git.yoctoproject.org/git-submodule-test;branch=ssh-gitsm-tests;rev=049da4a6cb198d7c0302e9e8b243a1443cb809a7;branch=master;protocol=https"
962 # Original URL (comment this if you have ssh access to git.yoctoproject.org) 1150 # Original URL (comment this if you have ssh access to git.yoctoproject.org)
963 url = "gitsm://git.yoctoproject.org/git-submodule-test;branch=master;rev=a2885dd7d25380d23627e7544b7bbb55014b16ee" 1151 url = "gitsm://git.yoctoproject.org/git-submodule-test;branch=master;rev=a2885dd7d25380d23627e7544b7bbb55014b16ee;branch=master;protocol=https"
964 fetcher = bb.fetch.Fetch([url], self.d) 1152 fetcher = bb.fetch.Fetch([url], self.d)
965 fetcher.download() 1153 fetcher.download()
966 # Previous cwd has been deleted 1154 # Previous cwd has been deleted
@@ -977,10 +1165,29 @@ class FetcherNetworkTest(FetcherTest):
977 self.assertTrue(os.path.exists(os.path.join(repo_path, 'bitbake-gitsm-test1', 'bitbake')), msg='submodule of submodule missing') 1165 self.assertTrue(os.path.exists(os.path.join(repo_path, 'bitbake-gitsm-test1', 'bitbake')), msg='submodule of submodule missing')
978 1166
979 @skipIfNoNetwork() 1167 @skipIfNoNetwork()
1168 def test_git_submodule_restricted_network_premirrors(self):
1169 # this test is to ensure that premirrors will be tried in restricted network
1170 # that is, BB_ALLOWED_NETWORKS does not contain the domain the url uses
1171 url = "gitsm://github.com/grpc/grpc.git;protocol=https;name=grpc;branch=v1.60.x;rev=0ef13a7555dbaadd4633399242524129eef5e231"
1172 # create a download directory to be used as premirror later
1173 tempdir = tempfile.mkdtemp(prefix="bitbake-fetch-")
1174 dl_premirror = os.path.join(tempdir, "download-premirror")
1175 os.mkdir(dl_premirror)
1176 self.d.setVar("DL_DIR", dl_premirror)
1177 fetcher = bb.fetch.Fetch([url], self.d)
1178 fetcher.download()
1179 # now use the premirror in restricted network
1180 self.d.setVar("DL_DIR", self.dldir)
1181 self.d.setVar("PREMIRRORS", "gitsm://.*/.* gitsm://%s/git2/MIRRORNAME;protocol=file" % dl_premirror)
1182 self.d.setVar("BB_ALLOWED_NETWORKS", "*.some.domain")
1183 fetcher = bb.fetch.Fetch([url], self.d)
1184 fetcher.download()
1185
1186 @skipIfNoNetwork()
980 def test_git_submodule_dbus_broker(self): 1187 def test_git_submodule_dbus_broker(self):
981 # The following external repositories have show failures in fetch and unpack operations 1188 # The following external repositories have show failures in fetch and unpack operations
982 # We want to avoid regressions! 1189 # We want to avoid regressions!
983 url = "gitsm://github.com/bus1/dbus-broker;protocol=git;rev=fc874afa0992d0c75ec25acb43d344679f0ee7d2;branch=main" 1190 url = "gitsm://github.com/bus1/dbus-broker;protocol=https;rev=fc874afa0992d0c75ec25acb43d344679f0ee7d2;branch=main"
984 fetcher = bb.fetch.Fetch([url], self.d) 1191 fetcher = bb.fetch.Fetch([url], self.d)
985 fetcher.download() 1192 fetcher.download()
986 # Previous cwd has been deleted 1193 # Previous cwd has been deleted
@@ -996,7 +1203,7 @@ class FetcherNetworkTest(FetcherTest):
996 1203
997 @skipIfNoNetwork() 1204 @skipIfNoNetwork()
998 def test_git_submodule_CLI11(self): 1205 def test_git_submodule_CLI11(self):
999 url = "gitsm://github.com/CLIUtils/CLI11;protocol=git;rev=bd4dc911847d0cde7a6b41dfa626a85aab213baf" 1206 url = "gitsm://github.com/CLIUtils/CLI11;protocol=https;rev=bd4dc911847d0cde7a6b41dfa626a85aab213baf;branch=main"
1000 fetcher = bb.fetch.Fetch([url], self.d) 1207 fetcher = bb.fetch.Fetch([url], self.d)
1001 fetcher.download() 1208 fetcher.download()
1002 # Previous cwd has been deleted 1209 # Previous cwd has been deleted
@@ -1011,12 +1218,12 @@ class FetcherNetworkTest(FetcherTest):
1011 @skipIfNoNetwork() 1218 @skipIfNoNetwork()
1012 def test_git_submodule_update_CLI11(self): 1219 def test_git_submodule_update_CLI11(self):
1013 """ Prevent regression on update detection not finding missing submodule, or modules without needed commits """ 1220 """ Prevent regression on update detection not finding missing submodule, or modules without needed commits """
1014 url = "gitsm://github.com/CLIUtils/CLI11;protocol=git;rev=cf6a99fa69aaefe477cc52e3ef4a7d2d7fa40714" 1221 url = "gitsm://github.com/CLIUtils/CLI11;protocol=https;rev=cf6a99fa69aaefe477cc52e3ef4a7d2d7fa40714;branch=main"
1015 fetcher = bb.fetch.Fetch([url], self.d) 1222 fetcher = bb.fetch.Fetch([url], self.d)
1016 fetcher.download() 1223 fetcher.download()
1017 1224
1018 # CLI11 that pulls in a newer nlohmann-json 1225 # CLI11 that pulls in a newer nlohmann-json
1019 url = "gitsm://github.com/CLIUtils/CLI11;protocol=git;rev=49ac989a9527ee9bb496de9ded7b4872c2e0e5ca" 1226 url = "gitsm://github.com/CLIUtils/CLI11;protocol=https;rev=49ac989a9527ee9bb496de9ded7b4872c2e0e5ca;branch=main"
1020 fetcher = bb.fetch.Fetch([url], self.d) 1227 fetcher = bb.fetch.Fetch([url], self.d)
1021 fetcher.download() 1228 fetcher.download()
1022 # Previous cwd has been deleted 1229 # Previous cwd has been deleted
@@ -1030,7 +1237,7 @@ class FetcherNetworkTest(FetcherTest):
1030 1237
1031 @skipIfNoNetwork() 1238 @skipIfNoNetwork()
1032 def test_git_submodule_aktualizr(self): 1239 def test_git_submodule_aktualizr(self):
1033 url = "gitsm://github.com/advancedtelematic/aktualizr;branch=master;protocol=git;rev=d00d1a04cc2366d1a5f143b84b9f507f8bd32c44" 1240 url = "gitsm://github.com/advancedtelematic/aktualizr;branch=master;protocol=https;rev=d00d1a04cc2366d1a5f143b84b9f507f8bd32c44"
1034 fetcher = bb.fetch.Fetch([url], self.d) 1241 fetcher = bb.fetch.Fetch([url], self.d)
1035 fetcher.download() 1242 fetcher.download()
1036 # Previous cwd has been deleted 1243 # Previous cwd has been deleted
@@ -1050,7 +1257,7 @@ class FetcherNetworkTest(FetcherTest):
1050 """ Prevent regression on deeply nested submodules not being checked out properly, even though they were fetched. """ 1257 """ Prevent regression on deeply nested submodules not being checked out properly, even though they were fetched. """
1051 1258
1052 # This repository also has submodules where the module (name), path and url do not align 1259 # This repository also has submodules where the module (name), path and url do not align
1053 url = "gitsm://github.com/azure/iotedge.git;protocol=git;rev=d76e0316c6f324345d77c48a83ce836d09392699" 1260 url = "gitsm://github.com/azure/iotedge.git;protocol=https;rev=d76e0316c6f324345d77c48a83ce836d09392699;branch=main"
1054 fetcher = bb.fetch.Fetch([url], self.d) 1261 fetcher = bb.fetch.Fetch([url], self.d)
1055 fetcher.download() 1262 fetcher.download()
1056 # Previous cwd has been deleted 1263 # Previous cwd has been deleted
@@ -1073,9 +1280,17 @@ class FetcherNetworkTest(FetcherTest):
1073 self.assertTrue(os.path.exists(os.path.join(repo_path, 'edgelet/hsm-sys/azure-iot-hsm-c/deps/utpm/deps/c-utility/testtools/umock-c/deps/ctest/README.md')), msg='Missing submodule checkout') 1280 self.assertTrue(os.path.exists(os.path.join(repo_path, 'edgelet/hsm-sys/azure-iot-hsm-c/deps/utpm/deps/c-utility/testtools/umock-c/deps/ctest/README.md')), msg='Missing submodule checkout')
1074 self.assertTrue(os.path.exists(os.path.join(repo_path, 'edgelet/hsm-sys/azure-iot-hsm-c/deps/utpm/deps/c-utility/testtools/umock-c/deps/testrunner/readme.md')), msg='Missing submodule checkout') 1281 self.assertTrue(os.path.exists(os.path.join(repo_path, 'edgelet/hsm-sys/azure-iot-hsm-c/deps/utpm/deps/c-utility/testtools/umock-c/deps/testrunner/readme.md')), msg='Missing submodule checkout')
1075 1282
1283 @skipIfNoNetwork()
1284 def test_git_submodule_reference_to_parent(self):
1285 self.recipe_url = "gitsm://github.com/gflags/gflags.git;protocol=https;branch=master"
1286 self.d.setVar("SRCREV", "14e1138441bbbb584160cb1c0a0426ec1bac35f1")
1287 with Timeout(60):
1288 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
1289 with self.assertRaises(bb.fetch2.FetchError):
1290 fetcher.download()
1291
1076class SVNTest(FetcherTest): 1292class SVNTest(FetcherTest):
1077 def skipIfNoSvn(): 1293 def skipIfNoSvn():
1078 import shutil
1079 if not shutil.which("svn"): 1294 if not shutil.which("svn"):
1080 return unittest.skip("svn not installed, tests being skipped") 1295 return unittest.skip("svn not installed, tests being skipped")
1081 1296
@@ -1107,8 +1322,9 @@ class SVNTest(FetcherTest):
1107 cwd=repo_dir) 1322 cwd=repo_dir)
1108 1323
1109 bb.process.run("svn co %s svnfetch_co" % self.repo_url, cwd=self.tempdir) 1324 bb.process.run("svn co %s svnfetch_co" % self.repo_url, cwd=self.tempdir)
1110 # Github will emulate SVN. Use this to check if we're downloding... 1325 # Github won't emulate SVN anymore (see https://github.blog/2023-01-20-sunsetting-subversion-support/)
1111 bb.process.run("svn propset svn:externals 'bitbake svn://vcs.pcre.org/pcre2/code' .", 1326 # Use still accessible svn repo (only trunk to avoid longer downloads)
1327 bb.process.run("svn propset svn:externals 'bitbake https://svn.apache.org/repos/asf/serf/trunk' .",
1112 cwd=os.path.join(self.tempdir, 'svnfetch_co', 'trunk')) 1328 cwd=os.path.join(self.tempdir, 'svnfetch_co', 'trunk'))
1113 bb.process.run("svn commit --non-interactive -m 'Add external'", 1329 bb.process.run("svn commit --non-interactive -m 'Add external'",
1114 cwd=os.path.join(self.tempdir, 'svnfetch_co', 'trunk')) 1330 cwd=os.path.join(self.tempdir, 'svnfetch_co', 'trunk'))
@@ -1136,8 +1352,8 @@ class SVNTest(FetcherTest):
1136 1352
1137 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk')), msg="Missing trunk") 1353 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk')), msg="Missing trunk")
1138 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk', 'README.md')), msg="Missing contents") 1354 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk', 'README.md')), msg="Missing contents")
1139 self.assertFalse(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/trunk')), msg="External dir should NOT exist") 1355 self.assertFalse(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/protocols')), msg="External dir should NOT exist")
1140 self.assertFalse(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/trunk', 'README')), msg="External README should NOT exit") 1356 self.assertFalse(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/protocols', 'fcgi_buckets.h')), msg="External fcgi_buckets.h should NOT exit")
1141 1357
1142 @skipIfNoSvn() 1358 @skipIfNoSvn()
1143 def test_external_svn(self): 1359 def test_external_svn(self):
@@ -1150,66 +1366,71 @@ class SVNTest(FetcherTest):
1150 1366
1151 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk')), msg="Missing trunk") 1367 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk')), msg="Missing trunk")
1152 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk', 'README.md')), msg="Missing contents") 1368 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk', 'README.md')), msg="Missing contents")
1153 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/trunk')), msg="External dir should exist") 1369 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/protocols')), msg="External dir should exist")
1154 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/trunk', 'README')), msg="External README should exit") 1370 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'trunk/bitbake/protocols', 'fcgi_buckets.h')), msg="External fcgi_buckets.h should exit")
1155 1371
1156class TrustedNetworksTest(FetcherTest): 1372class TrustedNetworksTest(FetcherTest):
1157 def test_trusted_network(self): 1373 def test_trusted_network(self):
1158 # Ensure trusted_network returns False when the host IS in the list. 1374 # Ensure trusted_network returns False when the host IS in the list.
1159 url = "git://Someserver.org/foo;rev=1" 1375 url = "git://Someserver.org/foo;rev=1;branch=master"
1160 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org someserver.org server2.org server3.org") 1376 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org someserver.org server2.org server3.org")
1161 self.assertTrue(bb.fetch.trusted_network(self.d, url)) 1377 self.assertTrue(bb.fetch.trusted_network(self.d, url))
1162 1378
1163 def test_wild_trusted_network(self): 1379 def test_wild_trusted_network(self):
1164 # Ensure trusted_network returns true when the *.host IS in the list. 1380 # Ensure trusted_network returns true when the *.host IS in the list.
1165 url = "git://Someserver.org/foo;rev=1" 1381 url = "git://Someserver.org/foo;rev=1;branch=master"
1166 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org") 1382 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
1167 self.assertTrue(bb.fetch.trusted_network(self.d, url)) 1383 self.assertTrue(bb.fetch.trusted_network(self.d, url))
1168 1384
1169 def test_prefix_wild_trusted_network(self): 1385 def test_prefix_wild_trusted_network(self):
1170 # Ensure trusted_network returns true when the prefix matches *.host. 1386 # Ensure trusted_network returns true when the prefix matches *.host.
1171 url = "git://git.Someserver.org/foo;rev=1" 1387 url = "git://git.Someserver.org/foo;rev=1;branch=master"
1172 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org") 1388 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
1173 self.assertTrue(bb.fetch.trusted_network(self.d, url)) 1389 self.assertTrue(bb.fetch.trusted_network(self.d, url))
1174 1390
1175 def test_two_prefix_wild_trusted_network(self): 1391 def test_two_prefix_wild_trusted_network(self):
1176 # Ensure trusted_network returns true when the prefix matches *.host. 1392 # Ensure trusted_network returns true when the prefix matches *.host.
1177 url = "git://something.git.Someserver.org/foo;rev=1" 1393 url = "git://something.git.Someserver.org/foo;rev=1;branch=master"
1178 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org") 1394 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org *.someserver.org server2.org server3.org")
1179 self.assertTrue(bb.fetch.trusted_network(self.d, url)) 1395 self.assertTrue(bb.fetch.trusted_network(self.d, url))
1180 1396
1181 def test_port_trusted_network(self): 1397 def test_port_trusted_network(self):
1182 # Ensure trusted_network returns True, even if the url specifies a port. 1398 # Ensure trusted_network returns True, even if the url specifies a port.
1183 url = "git://someserver.org:8080/foo;rev=1" 1399 url = "git://someserver.org:8080/foo;rev=1;branch=master"
1184 self.d.setVar("BB_ALLOWED_NETWORKS", "someserver.org") 1400 self.d.setVar("BB_ALLOWED_NETWORKS", "someserver.org")
1185 self.assertTrue(bb.fetch.trusted_network(self.d, url)) 1401 self.assertTrue(bb.fetch.trusted_network(self.d, url))
1186 1402
1187 def test_untrusted_network(self): 1403 def test_untrusted_network(self):
1188 # Ensure trusted_network returns False when the host is NOT in the list. 1404 # Ensure trusted_network returns False when the host is NOT in the list.
1189 url = "git://someserver.org/foo;rev=1" 1405 url = "git://someserver.org/foo;rev=1;branch=master"
1190 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org") 1406 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org")
1191 self.assertFalse(bb.fetch.trusted_network(self.d, url)) 1407 self.assertFalse(bb.fetch.trusted_network(self.d, url))
1192 1408
1193 def test_wild_untrusted_network(self): 1409 def test_wild_untrusted_network(self):
1194 # Ensure trusted_network returns False when the host is NOT in the list. 1410 # Ensure trusted_network returns False when the host is NOT in the list.
1195 url = "git://*.someserver.org/foo;rev=1" 1411 url = "git://*.someserver.org/foo;rev=1;branch=master"
1196 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org") 1412 self.d.setVar("BB_ALLOWED_NETWORKS", "server1.org server2.org server3.org")
1197 self.assertFalse(bb.fetch.trusted_network(self.d, url)) 1413 self.assertFalse(bb.fetch.trusted_network(self.d, url))
1198 1414
1199class URLHandle(unittest.TestCase): 1415class URLHandle(unittest.TestCase):
1200 1416 # Quote password as per RFC3986
1417 password = urllib.parse.quote(r"!#$%^&*()-_={}[]\|:?,.<>~`", r"!$&'/()*+,;=")
1201 datatable = { 1418 datatable = {
1202 "http://www.google.com/index.html" : ('http', 'www.google.com', '/index.html', '', '', {}), 1419 "http://www.google.com/index.html" : ('http', 'www.google.com', '/index.html', '', '', {}),
1203 "cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}), 1420 "cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}),
1204 "cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', collections.OrderedDict([('tag', 'V0-99-81'), ('module', 'familiar/dist/ipkg')])), 1421 "cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg" : ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', collections.OrderedDict([('tag', 'V0-99-81'), ('module', 'familiar/dist/ipkg')])),
1205 "git://git.openembedded.org/bitbake;branch=@foo" : ('git', 'git.openembedded.org', '/bitbake', '', '', {'branch': '@foo'}), 1422 "git://git.openembedded.org/bitbake;branch=@foo;protocol=https" : ('git', 'git.openembedded.org', '/bitbake', '', '', {'branch': '@foo', 'protocol' : 'https'}),
1206 "file://somelocation;someparam=1": ('file', '', 'somelocation', '', '', {'someparam': '1'}), 1423 "file://somelocation;someparam=1": ('file', '', 'somelocation', '', '', {'someparam': '1'}),
1424 "file://example@.service": ('file', '', 'example@.service', '', '', {}),
1425 "https://somesite.com/somerepo.git;user=anyUser:idtoken=1234" : ('https', 'somesite.com', '/somerepo.git', '', '', {'user': 'anyUser:idtoken=1234'}),
1426 'git://s.o-me_ONE:%s@git.openembedded.org/bitbake;branch=main;protocol=https' % password: ('git', 'git.openembedded.org', '/bitbake', 's.o-me_ONE', password, {'branch': 'main', 'protocol' : 'https'}),
1207 } 1427 }
1208 # we require a pathname to encodeurl but users can still pass such urls to 1428 # we require a pathname to encodeurl but users can still pass such urls to
1209 # decodeurl and we need to handle them 1429 # decodeurl and we need to handle them
1210 decodedata = datatable.copy() 1430 decodedata = datatable.copy()
1211 decodedata.update({ 1431 decodedata.update({
1212 "http://somesite.net;someparam=1": ('http', 'somesite.net', '/', '', '', {'someparam': '1'}), 1432 "http://somesite.net;someparam=1": ('http', 'somesite.net', '/', '', '', {'someparam': '1'}),
1433 "npmsw://some.registry.url;package=@pkg;version=latest": ('npmsw', 'some.registry.url', '/', '', '', {'package': '@pkg', 'version': 'latest'}),
1213 }) 1434 })
1214 1435
1215 def test_decodeurl(self): 1436 def test_decodeurl(self):
@@ -1220,138 +1441,179 @@ class URLHandle(unittest.TestCase):
1220 def test_encodeurl(self): 1441 def test_encodeurl(self):
1221 for k, v in self.datatable.items(): 1442 for k, v in self.datatable.items():
1222 result = bb.fetch.encodeurl(v) 1443 result = bb.fetch.encodeurl(v)
1444 if result.startswith("file:"):
1445 result = urllib.parse.unquote(result)
1223 self.assertEqual(result, k) 1446 self.assertEqual(result, k)
1224 1447
1225class FetchLatestVersionTest(FetcherTest): 1448class FetchLatestVersionTest(FetcherTest):
1226 1449
1227 test_git_uris = { 1450 test_git_uris = {
1228 # version pattern "X.Y.Z" 1451 # version pattern "X.Y.Z"
1229 ("mx-1.0", "git://github.com/clutter-project/mx.git;branch=mx-1.4", "9b1db6b8060bd00b121a692f942404a24ae2960f", "") 1452 ("mx-1.0", "git://github.com/clutter-project/mx.git;branch=mx-1.4;protocol=https", "9b1db6b8060bd00b121a692f942404a24ae2960f", "", "")
1230 : "1.99.4", 1453 : "1.99.4",
1231 # version pattern "vX.Y" 1454 # version pattern "vX.Y"
1232 # mirror of git.infradead.org since network issues interfered with testing 1455 # mirror of git.infradead.org since network issues interfered with testing
1233 ("mtd-utils", "git://git.yoctoproject.org/mtd-utils.git", "ca39eb1d98e736109c64ff9c1aa2a6ecca222d8f", "") 1456 ("mtd-utils", "git://git.yoctoproject.org/mtd-utils.git;branch=master;protocol=https", "ca39eb1d98e736109c64ff9c1aa2a6ecca222d8f", "", "")
1234 : "1.5.0", 1457 : "1.5.0",
1235 # version pattern "pkg_name-X.Y" 1458 # version pattern "pkg_name-X.Y"
1236 # mirror of git://anongit.freedesktop.org/git/xorg/proto/presentproto since network issues interfered with testing 1459 # mirror of git://anongit.freedesktop.org/git/xorg/proto/presentproto since network issues interfered with testing
1237 ("presentproto", "git://git.yoctoproject.org/bbfetchtests-presentproto", "24f3a56e541b0a9e6c6ee76081f441221a120ef9", "") 1460 ("presentproto", "git://git.yoctoproject.org/bbfetchtests-presentproto;branch=master;protocol=https", "24f3a56e541b0a9e6c6ee76081f441221a120ef9", "", "")
1238 : "1.0", 1461 : "1.0",
1239 # version pattern "pkg_name-vX.Y.Z" 1462 # version pattern "pkg_name-vX.Y.Z"
1240 ("dtc", "git://git.qemu.org/dtc.git", "65cc4d2748a2c2e6f27f1cf39e07a5dbabd80ebf", "") 1463 ("dtc", "git://git.yoctoproject.org/bbfetchtests-dtc.git;branch=master;protocol=https", "65cc4d2748a2c2e6f27f1cf39e07a5dbabd80ebf", "", "")
1241 : "1.4.0", 1464 : "1.4.0",
1242 # combination version pattern 1465 # combination version pattern
1243 ("sysprof", "git://gitlab.gnome.org/GNOME/sysprof.git;protocol=https", "cd44ee6644c3641507fb53b8a2a69137f2971219", "") 1466 ("sysprof", "git://git.yoctoproject.org/sysprof.git;protocol=https;branch=master", "cd44ee6644c3641507fb53b8a2a69137f2971219", "", "")
1244 : "1.2.0", 1467 : "1.2.0",
1245 ("u-boot-mkimage", "git://git.denx.de/u-boot.git;branch=master;protocol=git", "62c175fbb8a0f9a926c88294ea9f7e88eb898f6c", "") 1468 ("u-boot-mkimage", "git://git.yoctoproject.org/bbfetchtests-u-boot.git;branch=master;protocol=https", "62c175fbb8a0f9a926c88294ea9f7e88eb898f6c", "", "")
1246 : "2014.01", 1469 : "2014.01",
1247 # version pattern "yyyymmdd" 1470 # version pattern "yyyymmdd"
1248 ("mobile-broadband-provider-info", "git://gitlab.gnome.org/GNOME/mobile-broadband-provider-info.git;protocol=https", "4ed19e11c2975105b71b956440acdb25d46a347d", "") 1471 ("mobile-broadband-provider-info", "git://git.yoctoproject.org/mobile-broadband-provider-info.git;protocol=https;branch=master", "4ed19e11c2975105b71b956440acdb25d46a347d", "", "")
1249 : "20120614", 1472 : "20120614",
1250 # packages with a valid UPSTREAM_CHECK_GITTAGREGEX 1473 # packages with a valid UPSTREAM_CHECK_GITTAGREGEX
1251 # mirror of git://anongit.freedesktop.org/xorg/driver/xf86-video-omap since network issues interfered with testing 1474 # mirror of git://anongit.freedesktop.org/xorg/driver/xf86-video-omap since network issues interfered with testing
1252 ("xf86-video-omap", "git://git.yoctoproject.org/bbfetchtests-xf86-video-omap", "ae0394e687f1a77e966cf72f895da91840dffb8f", "(?P<pver>(\d+\.(\d\.?)*))") 1475 ("xf86-video-omap", "git://git.yoctoproject.org/bbfetchtests-xf86-video-omap;branch=master;protocol=https", "ae0394e687f1a77e966cf72f895da91840dffb8f", r"(?P<pver>(\d+\.(\d\.?)*))", "")
1253 : "0.4.3", 1476 : "0.4.3",
1254 ("build-appliance-image", "git://git.yoctoproject.org/poky", "b37dd451a52622d5b570183a81583cc34c2ff555", "(?P<pver>(([0-9][\.|_]?)+[0-9]))") 1477 ("build-appliance-image", "git://git.yoctoproject.org/poky;branch=master;protocol=https", "b37dd451a52622d5b570183a81583cc34c2ff555", r"(?P<pver>(([0-9][\.|_]?)+[0-9]))", "")
1255 : "11.0.0", 1478 : "11.0.0",
1256 ("chkconfig-alternatives-native", "git://github.com/kergoth/chkconfig;branch=sysroot", "cd437ecbd8986c894442f8fce1e0061e20f04dee", "chkconfig\-(?P<pver>((\d+[\.\-_]*)+))") 1479 ("chkconfig-alternatives-native", "git://github.com/kergoth/chkconfig;branch=sysroot;protocol=https", "cd437ecbd8986c894442f8fce1e0061e20f04dee", r"chkconfig\-(?P<pver>((\d+[\.\-_]*)+))", "")
1257 : "1.3.59", 1480 : "1.3.59",
1258 ("remake", "git://github.com/rocky/remake.git", "f05508e521987c8494c92d9c2871aec46307d51d", "(?P<pver>(\d+\.(\d+\.)*\d*(\+dbg\d+(\.\d+)*)*))") 1481 ("remake", "git://github.com/rocky/remake.git;protocol=https;branch=master", "f05508e521987c8494c92d9c2871aec46307d51d", r"(?P<pver>(\d+\.(\d+\.)*\d*(\+dbg\d+(\.\d+)*)*))", "")
1259 : "3.82+dbg0.9", 1482 : "3.82+dbg0.9",
1483 ("sysdig", "git://github.com/draios/sysdig.git;branch=dev;protocol=https", "4fb6288275f567f63515df0ff0a6518043ecfa9b", r"^(?P<pver>\d+(\.\d+)+)", "10.0.0")
1484 : "0.28.0",
1260 } 1485 }
1261 1486
1487 WgetTestData = collections.namedtuple("WgetTestData", ["pn", "path", "pv", "check_uri", "check_regex"], defaults=[None, None, None])
1262 test_wget_uris = { 1488 test_wget_uris = {
1263 # 1489 #
1264 # packages with versions inside directory name 1490 # packages with versions inside directory name
1265 # 1491 #
1266 # http://kernel.org/pub/linux/utils/util-linux/v2.23/util-linux-2.24.2.tar.bz2 1492 # http://kernel.org/pub/linux/utils/util-linux/v2.23/util-linux-2.24.2.tar.bz2
1267 ("util-linux", "/pub/linux/utils/util-linux/v2.23/util-linux-2.24.2.tar.bz2", "", "") 1493 WgetTestData("util-linux", "/pub/linux/utils/util-linux/v2.23/util-linux-2.24.2.tar.bz2")
1268 : "2.24.2", 1494 : "2.24.2",
1269 # http://www.abisource.com/downloads/enchant/1.6.0/enchant-1.6.0.tar.gz 1495 # http://www.abisource.com/downloads/enchant/1.6.0/enchant-1.6.0.tar.gz
1270 ("enchant", "/downloads/enchant/1.6.0/enchant-1.6.0.tar.gz", "", "") 1496 WgetTestData("enchant", "/downloads/enchant/1.6.0/enchant-1.6.0.tar.gz")
1271 : "1.6.0", 1497 : "1.6.0",
1272 # http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz 1498 # http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz
1273 ("cmake", "/files/v2.8/cmake-2.8.12.1.tar.gz", "", "") 1499 WgetTestData("cmake", "/files/v2.8/cmake-2.8.12.1.tar.gz")
1274 : "2.8.12.1", 1500 : "2.8.12.1",
1501 # https://download.gnome.org/sources/libxml2/2.9/libxml2-2.9.14.tar.xz
1502 WgetTestData("libxml2", "/software/libxml2/2.9/libxml2-2.9.14.tar.xz")
1503 : "2.10.3",
1275 # 1504 #
1276 # packages with versions only in current directory 1505 # packages with versions only in current directory
1277 # 1506 #
1278 # http://downloads.yoctoproject.org/releases/eglibc/eglibc-2.18-svnr23787.tar.bz2 1507 # https://downloads.yoctoproject.org/releases/eglibc/eglibc-2.18-svnr23787.tar.bz2
1279 ("eglic", "/releases/eglibc/eglibc-2.18-svnr23787.tar.bz2", "", "") 1508 WgetTestData("eglic", "/releases/eglibc/eglibc-2.18-svnr23787.tar.bz2")
1280 : "2.19", 1509 : "2.19",
1281 # http://downloads.yoctoproject.org/releases/gnu-config/gnu-config-20120814.tar.bz2 1510 # https://downloads.yoctoproject.org/releases/gnu-config/gnu-config-20120814.tar.bz2
1282 ("gnu-config", "/releases/gnu-config/gnu-config-20120814.tar.bz2", "", "") 1511 WgetTestData("gnu-config", "/releases/gnu-config/gnu-config-20120814.tar.bz2")
1283 : "20120814", 1512 : "20120814",
1284 # 1513 #
1285 # packages with "99" in the name of possible version 1514 # packages with "99" in the name of possible version
1286 # 1515 #
1287 # http://freedesktop.org/software/pulseaudio/releases/pulseaudio-4.0.tar.xz 1516 # http://freedesktop.org/software/pulseaudio/releases/pulseaudio-4.0.tar.xz
1288 ("pulseaudio", "/software/pulseaudio/releases/pulseaudio-4.0.tar.xz", "", "") 1517 WgetTestData("pulseaudio", "/software/pulseaudio/releases/pulseaudio-4.0.tar.xz")
1289 : "5.0", 1518 : "5.0",
1290 # http://xorg.freedesktop.org/releases/individual/xserver/xorg-server-1.15.1.tar.bz2 1519 # http://xorg.freedesktop.org/releases/individual/xserver/xorg-server-1.15.1.tar.bz2
1291 ("xserver-xorg", "/releases/individual/xserver/xorg-server-1.15.1.tar.bz2", "", "") 1520 WgetTestData("xserver-xorg", "/releases/individual/xserver/xorg-server-1.15.1.tar.bz2")
1292 : "1.15.1", 1521 : "1.15.1",
1293 # 1522 #
1294 # packages with valid UPSTREAM_CHECK_URI and UPSTREAM_CHECK_REGEX 1523 # packages with valid UPSTREAM_CHECK_URI and UPSTREAM_CHECK_REGEX
1295 # 1524 #
1296 # http://www.cups.org/software/1.7.2/cups-1.7.2-source.tar.bz2 1525 # http://www.cups.org/software/1.7.2/cups-1.7.2-source.tar.bz2
1297 # https://github.com/apple/cups/releases 1526 # https://github.com/apple/cups/releases
1298 ("cups", "/software/1.7.2/cups-1.7.2-source.tar.bz2", "/apple/cups/releases", "(?P<name>cups\-)(?P<pver>((\d+[\.\-_]*)+))\-source\.tar\.gz") 1527 WgetTestData("cups", "/software/1.7.2/cups-1.7.2-source.tar.bz2", check_uri="/apple/cups/releases", check_regex=r"(?P<name>cups\-)(?P<pver>((\d+[\.\-_]*)+))\-source\.tar\.gz")
1299 : "2.0.0", 1528 : "2.0.0",
1300 # http://download.oracle.com/berkeley-db/db-5.3.21.tar.gz 1529 # http://download.oracle.com/berkeley-db/db-5.3.21.tar.gz
1301 # http://ftp.debian.org/debian/pool/main/d/db5.3/ 1530 # http://ftp.debian.org/debian/pool/main/d/db5.3/
1302 ("db", "/berkeley-db/db-5.3.21.tar.gz", "/debian/pool/main/d/db5.3/", "(?P<name>db5\.3_)(?P<pver>\d+(\.\d+)+).+\.orig\.tar\.xz") 1531 WgetTestData("db", "/berkeley-db/db-5.3.21.tar.gz", check_uri="/debian/pool/main/d/db5.3/", check_regex=r"(?P<name>db5\.3_)(?P<pver>\d+(\.\d+)+).+\.orig\.tar\.xz")
1303 : "5.3.10", 1532 : "5.3.10",
1533 #
1534 # packages where the tarball compression changed in the new version
1535 #
1536 # http://ftp.debian.org/debian/pool/main/m/minicom/minicom_2.7.1.orig.tar.gz
1537 WgetTestData("minicom", "/debian/pool/main/m/minicom/minicom_2.7.1.orig.tar.gz")
1538 : "2.8",
1539
1540 #
1541 # packages where the path doesn't actually contain the filename, so downloadfilename should be respected
1542 #
1543 WgetTestData("miniupnpd", "/software/miniupnp/download.php?file=miniupnpd_2.1.20191006.tar.gz;downloadfilename=miniupnpd_2.1.20191006.tar.gz", pv="2.1.20191006", check_uri="/software/miniupnp/download.php", check_regex=r"miniupnpd-(?P<pver>\d+(\.\d+)+)\.tar")
1544 : "2.3.7",
1304 } 1545 }
1305 1546
1547 test_crate_uris = {
1548 # basic example; version pattern "A.B.C+cargo-D.E.F"
1549 ("cargo-c", "crate://crates.io/cargo-c/0.9.18+cargo-0.69")
1550 : "0.9.29"
1551 }
1552
1306 @skipIfNoNetwork() 1553 @skipIfNoNetwork()
1307 def test_git_latest_versionstring(self): 1554 def test_git_latest_versionstring(self):
1308 for k, v in self.test_git_uris.items(): 1555 for k, v in self.test_git_uris.items():
1309 self.d.setVar("PN", k[0]) 1556 with self.subTest(pn=k[0]):
1310 self.d.setVar("SRCREV", k[2]) 1557 self.d.setVar("PN", k[0])
1311 self.d.setVar("UPSTREAM_CHECK_GITTAGREGEX", k[3]) 1558 self.d.setVar("SRCREV", k[2])
1312 ud = bb.fetch2.FetchData(k[1], self.d) 1559 self.d.setVar("UPSTREAM_CHECK_GITTAGREGEX", k[3])
1313 pupver= ud.method.latest_versionstring(ud, self.d) 1560 ud = bb.fetch2.FetchData(k[1], self.d)
1314 verstring = pupver[0] 1561 pupver= ud.method.latest_versionstring(ud, self.d)
1315 self.assertTrue(verstring, msg="Could not find upstream version for %s" % k[0]) 1562 verstring = pupver[0]
1316 r = bb.utils.vercmp_string(v, verstring) 1563 self.assertTrue(verstring, msg="Could not find upstream version for %s" % k[0])
1317 self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring)) 1564 r = bb.utils.vercmp_string(v, verstring)
1565 self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring))
1566 if k[4]:
1567 r = bb.utils.vercmp_string(verstring, k[4])
1568 self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], verstring, k[4]))
1318 1569
1319 def test_wget_latest_versionstring(self): 1570 def test_wget_latest_versionstring(self):
1320 testdata = os.path.dirname(os.path.abspath(__file__)) + "/fetch-testdata" 1571 testdata = os.path.dirname(os.path.abspath(__file__)) + "/fetch-testdata"
1321 server = HTTPService(testdata) 1572 server = HTTPService(testdata, host="127.0.0.1")
1322 server.start() 1573 server.start()
1323 port = server.port 1574 port = server.port
1324 try: 1575 try:
1325 for k, v in self.test_wget_uris.items(): 1576 for data, v in self.test_wget_uris.items():
1577 with self.subTest(pn=data.pn):
1578 self.d.setVar("PN", data.pn)
1579 self.d.setVar("PV", data.pv)
1580 if data.check_uri:
1581 checkuri = "http://127.0.0.1:%s/%s" % (port, data.check_uri)
1582 self.d.setVar("UPSTREAM_CHECK_URI", checkuri)
1583 if data.check_regex:
1584 self.d.setVar("UPSTREAM_CHECK_REGEX", data.check_regex)
1585
1586 url = "http://127.0.0.1:%s/%s" % (port, data.path)
1587 ud = bb.fetch2.FetchData(url, self.d)
1588 pupver = ud.method.latest_versionstring(ud, self.d)
1589 verstring = pupver[0]
1590 self.assertTrue(verstring, msg="Could not find upstream version for %s" % data.pn)
1591 r = bb.utils.vercmp_string(v, verstring)
1592 self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (data.pn, v, verstring))
1593 finally:
1594 server.stop()
1595
1596 @skipIfNoNetwork()
1597 def test_crate_latest_versionstring(self):
1598 for k, v in self.test_crate_uris.items():
1599 with self.subTest(pn=k[0]):
1326 self.d.setVar("PN", k[0]) 1600 self.d.setVar("PN", k[0])
1327 checkuri = "" 1601 ud = bb.fetch2.FetchData(k[1], self.d)
1328 if k[2]:
1329 checkuri = "http://localhost:%s/" % port + k[2]
1330 self.d.setVar("UPSTREAM_CHECK_URI", checkuri)
1331 self.d.setVar("UPSTREAM_CHECK_REGEX", k[3])
1332 url = "http://localhost:%s/" % port + k[1]
1333 ud = bb.fetch2.FetchData(url, self.d)
1334 pupver = ud.method.latest_versionstring(ud, self.d) 1602 pupver = ud.method.latest_versionstring(ud, self.d)
1335 verstring = pupver[0] 1603 verstring = pupver[0]
1336 self.assertTrue(verstring, msg="Could not find upstream version for %s" % k[0]) 1604 self.assertTrue(verstring, msg="Could not find upstream version for %s" % k[0])
1337 r = bb.utils.vercmp_string(v, verstring) 1605 r = bb.utils.vercmp_string(v, verstring)
1338 self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring)) 1606 self.assertTrue(r == -1 or r == 0, msg="Package %s, version: %s <= %s" % (k[0], v, verstring))
1339 finally:
1340 server.stop()
1341
1342 1607
1343class FetchCheckStatusTest(FetcherTest): 1608class FetchCheckStatusTest(FetcherTest):
1344 test_wget_uris = ["http://downloads.yoctoproject.org/releases/sato/sato-engine-0.1.tar.gz", 1609 test_wget_uris = ["https://downloads.yoctoproject.org/releases/sato/sato-engine-0.1.tar.gz",
1345 "http://downloads.yoctoproject.org/releases/sato/sato-engine-0.2.tar.gz", 1610 "https://downloads.yoctoproject.org/releases/sato/sato-engine-0.2.tar.gz",
1346 "http://downloads.yoctoproject.org/releases/sato/sato-engine-0.3.tar.gz", 1611 "https://downloads.yoctoproject.org/releases/sato/sato-engine-0.3.tar.gz",
1347 "https://yoctoproject.org/", 1612 "https://yoctoproject.org/",
1348 "https://yoctoproject.org/documentation", 1613 "https://docs.yoctoproject.org",
1349 "http://downloads.yoctoproject.org/releases/opkg/opkg-0.1.7.tar.gz", 1614 "https://downloads.yoctoproject.org/releases/opkg/opkg-0.1.7.tar.gz",
1350 "http://downloads.yoctoproject.org/releases/opkg/opkg-0.3.0.tar.gz", 1615 "https://downloads.yoctoproject.org/releases/opkg/opkg-0.3.0.tar.gz",
1351 "ftp://sourceware.org/pub/libffi/libffi-1.20.tar.gz", 1616 "ftp://sourceware.org/pub/libffi/libffi-1.20.tar.gz",
1352 "http://ftp.gnu.org/gnu/autoconf/autoconf-2.60.tar.gz",
1353 "https://ftp.gnu.org/gnu/chess/gnuchess-5.08.tar.gz",
1354 "https://ftp.gnu.org/gnu/gmp/gmp-4.0.tar.gz",
1355 # GitHub releases are hosted on Amazon S3, which doesn't support HEAD 1617 # GitHub releases are hosted on Amazon S3, which doesn't support HEAD
1356 "https://github.com/kergoth/tslib/releases/download/1.1/tslib-1.1.tar.xz" 1618 "https://github.com/kergoth/tslib/releases/download/1.1/tslib-1.1.tar.xz"
1357 ] 1619 ]
@@ -1389,7 +1651,7 @@ class GitMakeShallowTest(FetcherTest):
1389 FetcherTest.setUp(self) 1651 FetcherTest.setUp(self)
1390 self.gitdir = os.path.join(self.tempdir, 'gitshallow') 1652 self.gitdir = os.path.join(self.tempdir, 'gitshallow')
1391 bb.utils.mkdirhier(self.gitdir) 1653 bb.utils.mkdirhier(self.gitdir)
1392 bb.process.run('git init', cwd=self.gitdir) 1654 self.git_init()
1393 1655
1394 def assertRefs(self, expected_refs): 1656 def assertRefs(self, expected_refs):
1395 actual_refs = self.git(['for-each-ref', '--format=%(refname)']).splitlines() 1657 actual_refs = self.git(['for-each-ref', '--format=%(refname)']).splitlines()
@@ -1403,13 +1665,6 @@ class GitMakeShallowTest(FetcherTest):
1403 actual_count = len(revs.splitlines()) 1665 actual_count = len(revs.splitlines())
1404 self.assertEqual(expected_count, actual_count, msg='Object count `%d` is not the expected `%d`' % (actual_count, expected_count)) 1666 self.assertEqual(expected_count, actual_count, msg='Object count `%d` is not the expected `%d`' % (actual_count, expected_count))
1405 1667
1406 def git(self, cmd):
1407 if isinstance(cmd, str):
1408 cmd = 'git ' + cmd
1409 else:
1410 cmd = ['git'] + cmd
1411 return bb.process.run(cmd, cwd=self.gitdir)[0]
1412
1413 def make_shallow(self, args=None): 1668 def make_shallow(self, args=None):
1414 if args is None: 1669 if args is None:
1415 args = ['HEAD'] 1670 args = ['HEAD']
@@ -1512,13 +1767,13 @@ class GitShallowTest(FetcherTest):
1512 self.srcdir = os.path.join(self.tempdir, 'gitsource') 1767 self.srcdir = os.path.join(self.tempdir, 'gitsource')
1513 1768
1514 bb.utils.mkdirhier(self.srcdir) 1769 bb.utils.mkdirhier(self.srcdir)
1515 self.git('init', cwd=self.srcdir) 1770 self.git_init(cwd=self.srcdir)
1516 self.d.setVar('WORKDIR', self.tempdir) 1771 self.d.setVar('WORKDIR', self.tempdir)
1517 self.d.setVar('S', self.gitdir) 1772 self.d.setVar('S', self.gitdir)
1518 self.d.delVar('PREMIRRORS') 1773 self.d.delVar('PREMIRRORS')
1519 self.d.delVar('MIRRORS') 1774 self.d.delVar('MIRRORS')
1520 1775
1521 uri = 'git://%s;protocol=file;subdir=${S}' % self.srcdir 1776 uri = 'git://%s;protocol=file;subdir=${S};branch=master' % self.srcdir
1522 self.d.setVar('SRC_URI', uri) 1777 self.d.setVar('SRC_URI', uri)
1523 self.d.setVar('SRCREV', '${AUTOREV}') 1778 self.d.setVar('SRCREV', '${AUTOREV}')
1524 self.d.setVar('AUTOREV', '${@bb.fetch2.get_autorev(d)}') 1779 self.d.setVar('AUTOREV', '${@bb.fetch2.get_autorev(d)}')
@@ -1526,11 +1781,14 @@ class GitShallowTest(FetcherTest):
1526 self.d.setVar('BB_GIT_SHALLOW', '1') 1781 self.d.setVar('BB_GIT_SHALLOW', '1')
1527 self.d.setVar('BB_GENERATE_MIRROR_TARBALLS', '0') 1782 self.d.setVar('BB_GENERATE_MIRROR_TARBALLS', '0')
1528 self.d.setVar('BB_GENERATE_SHALLOW_TARBALLS', '1') 1783 self.d.setVar('BB_GENERATE_SHALLOW_TARBALLS', '1')
1784 self.d.setVar("__BBSRCREV_SEEN", "1")
1529 1785
1530 def assertRefs(self, expected_refs, cwd=None): 1786 def assertRefs(self, expected_refs, cwd=None):
1531 if cwd is None: 1787 if cwd is None:
1532 cwd = self.gitdir 1788 cwd = self.gitdir
1533 actual_refs = self.git(['for-each-ref', '--format=%(refname)'], cwd=cwd).splitlines() 1789 actual_refs = self.git(['for-each-ref', '--format=%(refname)'], cwd=cwd).splitlines()
1790 # Resolve references into the same format as the comparision (needed by git 2.48 onwards)
1791 actual_refs = self.git(['rev-parse', '--symbolic-full-name'] + actual_refs, cwd=cwd).splitlines()
1534 full_expected = self.git(['rev-parse', '--symbolic-full-name'] + expected_refs, cwd=cwd).splitlines() 1792 full_expected = self.git(['rev-parse', '--symbolic-full-name'] + expected_refs, cwd=cwd).splitlines()
1535 self.assertEqual(sorted(set(full_expected)), sorted(set(actual_refs))) 1793 self.assertEqual(sorted(set(full_expected)), sorted(set(actual_refs)))
1536 1794
@@ -1543,15 +1801,6 @@ class GitShallowTest(FetcherTest):
1543 actual_count = len(revs.splitlines()) 1801 actual_count = len(revs.splitlines())
1544 self.assertEqual(expected_count, actual_count, msg='Object count `%d` is not the expected `%d`' % (actual_count, expected_count)) 1802 self.assertEqual(expected_count, actual_count, msg='Object count `%d` is not the expected `%d`' % (actual_count, expected_count))
1545 1803
1546 def git(self, cmd, cwd=None):
1547 if isinstance(cmd, str):
1548 cmd = 'git ' + cmd
1549 else:
1550 cmd = ['git'] + cmd
1551 if cwd is None:
1552 cwd = self.gitdir
1553 return bb.process.run(cmd, cwd=cwd)[0]
1554
1555 def add_empty_file(self, path, cwd=None, msg=None): 1804 def add_empty_file(self, path, cwd=None, msg=None):
1556 if msg is None: 1805 if msg is None:
1557 msg = path 1806 msg = path
@@ -1586,7 +1835,6 @@ class GitShallowTest(FetcherTest):
1586 def fetch_shallow(self, uri=None, disabled=False, keepclone=False): 1835 def fetch_shallow(self, uri=None, disabled=False, keepclone=False):
1587 """Fetch a uri, generating a shallow tarball, then unpack using it""" 1836 """Fetch a uri, generating a shallow tarball, then unpack using it"""
1588 fetcher, ud = self.fetch_and_unpack(uri) 1837 fetcher, ud = self.fetch_and_unpack(uri)
1589 assert os.path.exists(ud.clonedir), 'Git clone in DLDIR (%s) does not exist for uri %s' % (ud.clonedir, uri)
1590 1838
1591 # Confirm that the unpacked repo is unshallow 1839 # Confirm that the unpacked repo is unshallow
1592 if not disabled: 1840 if not disabled:
@@ -1594,8 +1842,10 @@ class GitShallowTest(FetcherTest):
1594 1842
1595 # fetch and unpack, from the shallow tarball 1843 # fetch and unpack, from the shallow tarball
1596 bb.utils.remove(self.gitdir, recurse=True) 1844 bb.utils.remove(self.gitdir, recurse=True)
1597 bb.utils.remove(ud.clonedir, recurse=True) 1845 if os.path.exists(ud.clonedir):
1598 bb.utils.remove(ud.clonedir.replace('gitsource', 'gitsubmodule'), recurse=True) 1846 bb.process.run('chmod u+w -R "%s"' % ud.clonedir)
1847 bb.utils.remove(ud.clonedir, recurse=True)
1848 bb.utils.remove(ud.clonedir.replace('gitsource', 'gitsubmodule'), recurse=True)
1599 1849
1600 # confirm that the unpacked repo is used when no git clone or git 1850 # confirm that the unpacked repo is used when no git clone or git
1601 # mirror tarball is available 1851 # mirror tarball is available
@@ -1678,7 +1928,12 @@ class GitShallowTest(FetcherTest):
1678 self.add_empty_file('c') 1928 self.add_empty_file('c')
1679 self.assertRevCount(3, cwd=self.srcdir) 1929 self.assertRevCount(3, cwd=self.srcdir)
1680 1930
1931 # Clone without tarball
1932 self.d.setVar('BB_GIT_SHALLOW', '0')
1933 fetcher, ud = self.fetch()
1934
1681 # Clone and generate mirror tarball 1935 # Clone and generate mirror tarball
1936 self.d.setVar('BB_GIT_SHALLOW', '1')
1682 fetcher, ud = self.fetch() 1937 fetcher, ud = self.fetch()
1683 1938
1684 # Ensure we have a current mirror tarball, but an out of date clone 1939 # Ensure we have a current mirror tarball, but an out of date clone
@@ -1690,6 +1945,7 @@ class GitShallowTest(FetcherTest):
1690 fetcher, ud = self.fetch() 1945 fetcher, ud = self.fetch()
1691 fetcher.unpack(self.d.getVar('WORKDIR')) 1946 fetcher.unpack(self.d.getVar('WORKDIR'))
1692 self.assertRevCount(1) 1947 self.assertRevCount(1)
1948 assert os.path.exists(os.path.join(self.d.getVar('WORKDIR'), 'git', 'c'))
1693 1949
1694 def test_shallow_single_branch_no_merge(self): 1950 def test_shallow_single_branch_no_merge(self):
1695 self.add_empty_file('a') 1951 self.add_empty_file('a')
@@ -1745,7 +2001,7 @@ class GitShallowTest(FetcherTest):
1745 2001
1746 smdir = os.path.join(self.tempdir, 'gitsubmodule') 2002 smdir = os.path.join(self.tempdir, 'gitsubmodule')
1747 bb.utils.mkdirhier(smdir) 2003 bb.utils.mkdirhier(smdir)
1748 self.git('init', cwd=smdir) 2004 self.git_init(cwd=smdir)
1749 # Make this look like it was cloned from a remote... 2005 # Make this look like it was cloned from a remote...
1750 self.git('config --add remote.origin.url "%s"' % smdir, cwd=smdir) 2006 self.git('config --add remote.origin.url "%s"' % smdir, cwd=smdir)
1751 self.git('config --add remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', cwd=smdir) 2007 self.git('config --add remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', cwd=smdir)
@@ -1753,11 +2009,11 @@ class GitShallowTest(FetcherTest):
1753 self.add_empty_file('bsub', cwd=smdir) 2009 self.add_empty_file('bsub', cwd=smdir)
1754 2010
1755 self.git('submodule init', cwd=self.srcdir) 2011 self.git('submodule init', cwd=self.srcdir)
1756 self.git('submodule add file://%s' % smdir, cwd=self.srcdir) 2012 self.git('-c protocol.file.allow=always submodule add file://%s' % smdir, cwd=self.srcdir)
1757 self.git('submodule update', cwd=self.srcdir) 2013 self.git('submodule update', cwd=self.srcdir)
1758 self.git('commit -m submodule -a', cwd=self.srcdir) 2014 self.git('commit -m submodule -a', cwd=self.srcdir)
1759 2015
1760 uri = 'gitsm://%s;protocol=file;subdir=${S}' % self.srcdir 2016 uri = 'gitsm://%s;protocol=file;subdir=${S};branch=master' % self.srcdir
1761 fetcher, ud = self.fetch_shallow(uri) 2017 fetcher, ud = self.fetch_shallow(uri)
1762 2018
1763 # Verify the main repository is shallow 2019 # Verify the main repository is shallow
@@ -1775,7 +2031,7 @@ class GitShallowTest(FetcherTest):
1775 2031
1776 smdir = os.path.join(self.tempdir, 'gitsubmodule') 2032 smdir = os.path.join(self.tempdir, 'gitsubmodule')
1777 bb.utils.mkdirhier(smdir) 2033 bb.utils.mkdirhier(smdir)
1778 self.git('init', cwd=smdir) 2034 self.git_init(cwd=smdir)
1779 # Make this look like it was cloned from a remote... 2035 # Make this look like it was cloned from a remote...
1780 self.git('config --add remote.origin.url "%s"' % smdir, cwd=smdir) 2036 self.git('config --add remote.origin.url "%s"' % smdir, cwd=smdir)
1781 self.git('config --add remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', cwd=smdir) 2037 self.git('config --add remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', cwd=smdir)
@@ -1783,19 +2039,19 @@ class GitShallowTest(FetcherTest):
1783 self.add_empty_file('bsub', cwd=smdir) 2039 self.add_empty_file('bsub', cwd=smdir)
1784 2040
1785 self.git('submodule init', cwd=self.srcdir) 2041 self.git('submodule init', cwd=self.srcdir)
1786 self.git('submodule add file://%s' % smdir, cwd=self.srcdir) 2042 self.git('-c protocol.file.allow=always submodule add file://%s' % smdir, cwd=self.srcdir)
1787 self.git('submodule update', cwd=self.srcdir) 2043 self.git('submodule update', cwd=self.srcdir)
1788 self.git('commit -m submodule -a', cwd=self.srcdir) 2044 self.git('commit -m submodule -a', cwd=self.srcdir)
1789 2045
1790 uri = 'gitsm://%s;protocol=file;subdir=${S}' % self.srcdir 2046 uri = 'gitsm://%s;protocol=file;subdir=${S};branch=master' % self.srcdir
1791 2047
1792 # Fetch once to generate the shallow tarball 2048 # Fetch once to generate the shallow tarball
1793 fetcher, ud = self.fetch(uri) 2049 fetcher, ud = self.fetch(uri)
1794 2050
1795 # Set up the mirror 2051 # Set up the mirror
1796 mirrordir = os.path.join(self.tempdir, 'mirror') 2052 mirrordir = os.path.join(self.tempdir, 'mirror')
1797 os.rename(self.dldir, mirrordir) 2053 bb.utils.rename(self.dldir, mirrordir)
1798 self.d.setVar('PREMIRRORS', 'gitsm://.*/.* file://%s/\n' % mirrordir) 2054 self.d.setVar('PREMIRRORS', 'gitsm://.*/.* file://%s/' % mirrordir)
1799 2055
1800 # Fetch from the mirror 2056 # Fetch from the mirror
1801 bb.utils.remove(self.dldir, recurse=True) 2057 bb.utils.remove(self.dldir, recurse=True)
@@ -1818,80 +2074,27 @@ class GitShallowTest(FetcherTest):
1818 self.git('annex init', cwd=self.srcdir) 2074 self.git('annex init', cwd=self.srcdir)
1819 open(os.path.join(self.srcdir, 'c'), 'w').close() 2075 open(os.path.join(self.srcdir, 'c'), 'w').close()
1820 self.git('annex add c', cwd=self.srcdir) 2076 self.git('annex add c', cwd=self.srcdir)
1821 self.git('commit -m annex-c -a', cwd=self.srcdir) 2077 self.git('commit --author "Foo Bar <foo@bar>" -m annex-c -a', cwd=self.srcdir)
1822 bb.process.run('chmod u+w -R %s' % os.path.join(self.srcdir, '.git', 'annex')) 2078 bb.process.run('chmod u+w -R %s' % self.srcdir)
1823 2079
1824 uri = 'gitannex://%s;protocol=file;subdir=${S}' % self.srcdir 2080 uri = 'gitannex://%s;protocol=file;subdir=${S};branch=master' % self.srcdir
1825 fetcher, ud = self.fetch_shallow(uri) 2081 fetcher, ud = self.fetch_shallow(uri)
1826 2082
1827 self.assertRevCount(1) 2083 self.assertRevCount(1)
1828 assert './.git/annex/' in bb.process.run('tar -tzf %s' % os.path.join(self.dldir, ud.mirrortarballs[0]))[0] 2084 assert './.git/annex/' in bb.process.run('tar -tzf %s' % os.path.join(self.dldir, ud.mirrortarballs[0]))[0]
1829 assert os.path.exists(os.path.join(self.gitdir, 'c')) 2085 assert os.path.exists(os.path.join(self.gitdir, 'c'))
1830 2086
1831 def test_shallow_multi_one_uri(self):
1832 # Create initial git repo
1833 self.add_empty_file('a')
1834 self.add_empty_file('b')
1835 self.git('checkout -b a_branch', cwd=self.srcdir)
1836 self.add_empty_file('c')
1837 self.add_empty_file('d')
1838 self.git('checkout master', cwd=self.srcdir)
1839 self.git('tag v0.0 a_branch', cwd=self.srcdir)
1840 self.add_empty_file('e')
1841 self.git('merge --no-ff --no-edit a_branch', cwd=self.srcdir)
1842 self.add_empty_file('f')
1843 self.assertRevCount(7, cwd=self.srcdir)
1844
1845 uri = self.d.getVar('SRC_URI').split()[0]
1846 uri = '%s;branch=master,a_branch;name=master,a_branch' % uri
1847
1848 self.d.setVar('BB_GIT_SHALLOW_DEPTH', '0')
1849 self.d.setVar('BB_GIT_SHALLOW_REVS', 'v0.0')
1850 self.d.setVar('SRCREV_master', '${AUTOREV}')
1851 self.d.setVar('SRCREV_a_branch', '${AUTOREV}')
1852
1853 self.fetch_shallow(uri)
1854
1855 self.assertRevCount(5)
1856 self.assertRefs(['master', 'origin/master', 'origin/a_branch'])
1857
1858 def test_shallow_multi_one_uri_depths(self):
1859 # Create initial git repo
1860 self.add_empty_file('a')
1861 self.add_empty_file('b')
1862 self.git('checkout -b a_branch', cwd=self.srcdir)
1863 self.add_empty_file('c')
1864 self.add_empty_file('d')
1865 self.git('checkout master', cwd=self.srcdir)
1866 self.add_empty_file('e')
1867 self.git('merge --no-ff --no-edit a_branch', cwd=self.srcdir)
1868 self.add_empty_file('f')
1869 self.assertRevCount(7, cwd=self.srcdir)
1870
1871 uri = self.d.getVar('SRC_URI').split()[0]
1872 uri = '%s;branch=master,a_branch;name=master,a_branch' % uri
1873
1874 self.d.setVar('BB_GIT_SHALLOW_DEPTH', '0')
1875 self.d.setVar('BB_GIT_SHALLOW_DEPTH_master', '3')
1876 self.d.setVar('BB_GIT_SHALLOW_DEPTH_a_branch', '1')
1877 self.d.setVar('SRCREV_master', '${AUTOREV}')
1878 self.d.setVar('SRCREV_a_branch', '${AUTOREV}')
1879
1880 self.fetch_shallow(uri)
1881
1882 self.assertRevCount(4, ['--all'])
1883 self.assertRefs(['master', 'origin/master', 'origin/a_branch'])
1884
1885 def test_shallow_clone_preferred_over_shallow(self): 2087 def test_shallow_clone_preferred_over_shallow(self):
1886 self.add_empty_file('a') 2088 self.add_empty_file('a')
1887 self.add_empty_file('b') 2089 self.add_empty_file('b')
1888 2090
1889 # Fetch once to generate the shallow tarball 2091 # Fetch once to generate the shallow tarball
2092 self.d.setVar('BB_GIT_SHALLOW', '0')
1890 fetcher, ud = self.fetch() 2093 fetcher, ud = self.fetch()
1891 assert os.path.exists(os.path.join(self.dldir, ud.mirrortarballs[0]))
1892 2094
1893 # Fetch and unpack with both the clonedir and shallow tarball available 2095 # Fetch and unpack with both the clonedir and shallow tarball available
1894 bb.utils.remove(self.gitdir, recurse=True) 2096 bb.utils.remove(self.gitdir, recurse=True)
2097 self.d.setVar('BB_GIT_SHALLOW', '1')
1895 fetcher, ud = self.fetch_and_unpack() 2098 fetcher, ud = self.fetch_and_unpack()
1896 2099
1897 # The unpacked tree should *not* be shallow 2100 # The unpacked tree should *not* be shallow
@@ -1910,9 +2113,9 @@ class GitShallowTest(FetcherTest):
1910 # Set up the mirror 2113 # Set up the mirror
1911 mirrordir = os.path.join(self.tempdir, 'mirror') 2114 mirrordir = os.path.join(self.tempdir, 'mirror')
1912 bb.utils.mkdirhier(mirrordir) 2115 bb.utils.mkdirhier(mirrordir)
1913 self.d.setVar('PREMIRRORS', 'git://.*/.* file://%s/\n' % mirrordir) 2116 self.d.setVar('PREMIRRORS', 'git://.*/.* file://%s/' % mirrordir)
1914 2117
1915 os.rename(os.path.join(self.dldir, mirrortarball), 2118 bb.utils.rename(os.path.join(self.dldir, mirrortarball),
1916 os.path.join(mirrordir, mirrortarball)) 2119 os.path.join(mirrordir, mirrortarball))
1917 2120
1918 # Fetch from the mirror 2121 # Fetch from the mirror
@@ -1999,7 +2202,7 @@ class GitShallowTest(FetcherTest):
1999 2202
2000 self.fetch_shallow() 2203 self.fetch_shallow()
2001 2204
2002 self.assertRevCount(5) 2205 self.assertRevCount(2)
2003 2206
2004 def test_shallow_invalid_revs(self): 2207 def test_shallow_invalid_revs(self):
2005 self.add_empty_file('a') 2208 self.add_empty_file('a')
@@ -2018,7 +2221,10 @@ class GitShallowTest(FetcherTest):
2018 self.git('tag v0.0 master', cwd=self.srcdir) 2221 self.git('tag v0.0 master', cwd=self.srcdir)
2019 self.d.setVar('BB_GIT_SHALLOW_DEPTH', '0') 2222 self.d.setVar('BB_GIT_SHALLOW_DEPTH', '0')
2020 self.d.setVar('BB_GIT_SHALLOW_REVS', 'v0.0') 2223 self.d.setVar('BB_GIT_SHALLOW_REVS', 'v0.0')
2021 self.fetch_shallow() 2224
2225 with self.assertRaises(bb.fetch2.FetchError), self.assertLogs("BitBake.Fetcher", level="ERROR") as cm:
2226 self.fetch_shallow()
2227 self.assertIn("fatal: no commits selected for shallow requests", cm.output[0])
2022 2228
2023 def test_shallow_fetch_missing_revs_fails(self): 2229 def test_shallow_fetch_missing_revs_fails(self):
2024 self.add_empty_file('a') 2230 self.add_empty_file('a')
@@ -2032,8 +2238,35 @@ class GitShallowTest(FetcherTest):
2032 self.assertIn("Unable to find revision v0.0 even from upstream", cm.output[0]) 2238 self.assertIn("Unable to find revision v0.0 even from upstream", cm.output[0])
2033 2239
2034 @skipIfNoNetwork() 2240 @skipIfNoNetwork()
2241 def test_git_shallow_fetch_premirrors(self):
2242 url = "git://git.openembedded.org/bitbake;branch=master;protocol=https"
2243
2244 # Create a separate premirror directory within tempdir
2245 premirror = os.path.join(self.tempdir, "premirror")
2246 os.mkdir(premirror)
2247
2248 # Fetch a non-shallow clone into the premirror subdir
2249 self.d.setVar('BB_GIT_SHALLOW', '0')
2250 self.d.setVar("DL_DIR", premirror)
2251 fetcher, ud = self.fetch(url)
2252
2253 # Fetch a shallow clone from the premirror subdir with unpacking
2254 # using the original recipe URL and the premirror mapping
2255 self.d.setVar('BB_GIT_SHALLOW', '1')
2256 self.d.setVar("DL_DIR", self.dldir)
2257 self.d.setVar('BB_FETCH_PREMIRRORONLY', '1')
2258 self.d.setVar('BB_NO_NETWORK', '1')
2259 self.d.setVar('BB_GENERATE_MIRROR_TARBALLS', '0')
2260 self.d.setVar("PREMIRRORS", "git://.*/.* git://{0};protocol=file".format(premirror + "/git2/" + ud.host + ud.path.replace("/", ".")))
2261 fetcher = self.fetch_and_unpack(url)
2262
2263 # Verify that the unpacked sources are shallow clones
2264 self.assertRevCount(1)
2265 assert os.path.exists(os.path.join(self.gitdir, '.git', 'shallow'))
2266
2267 @skipIfNoNetwork()
2035 def test_bitbake(self): 2268 def test_bitbake(self):
2036 self.git('remote add --mirror=fetch origin git://github.com/openembedded/bitbake', cwd=self.srcdir) 2269 self.git('remote add --mirror=fetch origin https://github.com/openembedded/bitbake', cwd=self.srcdir)
2037 self.git('config core.bare true', cwd=self.srcdir) 2270 self.git('config core.bare true', cwd=self.srcdir)
2038 self.git('fetch', cwd=self.srcdir) 2271 self.git('fetch', cwd=self.srcdir)
2039 2272
@@ -2049,7 +2282,7 @@ class GitShallowTest(FetcherTest):
2049 revs = len(self.git('rev-list master').splitlines()) 2282 revs = len(self.git('rev-list master').splitlines())
2050 self.assertNotEqual(orig_revs, revs) 2283 self.assertNotEqual(orig_revs, revs)
2051 self.assertRefs(['master', 'origin/master']) 2284 self.assertRefs(['master', 'origin/master'])
2052 self.assertRevCount(orig_revs - 1758) 2285 self.assertRevCount(orig_revs - 1760)
2053 2286
2054 def test_that_unpack_throws_an_error_when_the_git_clone_nor_shallow_tarball_exist(self): 2287 def test_that_unpack_throws_an_error_when_the_git_clone_nor_shallow_tarball_exist(self):
2055 self.add_empty_file('a') 2288 self.add_empty_file('a')
@@ -2063,27 +2296,43 @@ class GitShallowTest(FetcherTest):
2063 self.assertIn("No up to date source found", context.exception.msg) 2296 self.assertIn("No up to date source found", context.exception.msg)
2064 self.assertIn("clone directory not available or not up to date", context.exception.msg) 2297 self.assertIn("clone directory not available or not up to date", context.exception.msg)
2065 2298
2066 @skipIfNoNetwork() 2299 def test_shallow_check_is_shallow(self):
2067 def test_that_unpack_does_work_when_using_git_shallow_tarball_but_tarball_is_not_available(self): 2300 self.add_empty_file('a')
2068 self.d.setVar('SRCREV', 'e5939ff608b95cdd4d0ab0e1935781ab9a276ac0') 2301 self.add_empty_file('b')
2069 self.d.setVar('BB_GIT_SHALLOW', '1')
2070 self.d.setVar('BB_GENERATE_SHALLOW_TARBALLS', '1')
2071 fetcher = bb.fetch.Fetch(["git://git.yoctoproject.org/fstests"], self.d)
2072 fetcher.download()
2073 2302
2074 bb.utils.remove(self.dldir + "/*.tar.gz") 2303 # Fetch and unpack without the clonedir and *only* shallow tarball available
2075 fetcher.unpack(self.unpackdir) 2304 bb.utils.remove(self.gitdir, recurse=True)
2305 fetcher, ud = self.fetch_and_unpack()
2306
2307 # The unpacked tree *should* be shallow
2308 self.assertRevCount(1)
2309 assert os.path.exists(os.path.join(self.gitdir, '.git', 'shallow'))
2310
2311 def test_shallow_succeeds_with_tag_containing_slash(self):
2312 self.add_empty_file('a')
2313 self.add_empty_file('b')
2314 self.git('tag t1/t2/t3', cwd=self.srcdir)
2315 self.assertRevCount(2, cwd=self.srcdir)
2076 2316
2077 dir = os.listdir(self.unpackdir + "/git/") 2317 srcrev = self.git('rev-parse HEAD', cwd=self.srcdir).strip()
2078 self.assertIn("fstests.doap", dir) 2318 self.d.setVar('SRCREV', srcrev)
2319 uri = self.d.getVar('SRC_URI').split()[0]
2320 uri = '%s;tag=t1/t2/t3' % uri
2321 self.fetch_shallow(uri)
2322 self.assertRevCount(1)
2079 2323
2080class GitLfsTest(FetcherTest): 2324class GitLfsTest(FetcherTest):
2325 def skipIfNoGitLFS():
2326 if not shutil.which('git-lfs'):
2327 return unittest.skip('git-lfs not installed')
2328 return lambda f: f
2329
2081 def setUp(self): 2330 def setUp(self):
2082 FetcherTest.setUp(self) 2331 FetcherTest.setUp(self)
2083 2332
2084 self.gitdir = os.path.join(self.tempdir, 'git') 2333 self.gitdir = os.path.join(self.tempdir, 'git')
2085 self.srcdir = os.path.join(self.tempdir, 'gitsource') 2334 self.srcdir = os.path.join(self.tempdir, 'gitsource')
2086 2335
2087 self.d.setVar('WORKDIR', self.tempdir) 2336 self.d.setVar('WORKDIR', self.tempdir)
2088 self.d.setVar('S', self.gitdir) 2337 self.d.setVar('S', self.gitdir)
2089 self.d.delVar('PREMIRRORS') 2338 self.d.delVar('PREMIRRORS')
@@ -2091,22 +2340,24 @@ class GitLfsTest(FetcherTest):
2091 2340
2092 self.d.setVar('SRCREV', '${AUTOREV}') 2341 self.d.setVar('SRCREV', '${AUTOREV}')
2093 self.d.setVar('AUTOREV', '${@bb.fetch2.get_autorev(d)}') 2342 self.d.setVar('AUTOREV', '${@bb.fetch2.get_autorev(d)}')
2343 self.d.setVar("__BBSRCREV_SEEN", "1")
2094 2344
2095 bb.utils.mkdirhier(self.srcdir) 2345 bb.utils.mkdirhier(self.srcdir)
2096 self.git('init', cwd=self.srcdir) 2346 self.git_init(cwd=self.srcdir)
2097 with open(os.path.join(self.srcdir, '.gitattributes'), 'wt') as attrs: 2347 self.commit_file('.gitattributes', '*.mp3 filter=lfs -text')
2098 attrs.write('*.mp3 filter=lfs -text')
2099 self.git(['add', '.gitattributes'], cwd=self.srcdir)
2100 self.git(['commit', '-m', "attributes", '.gitattributes'], cwd=self.srcdir)
2101 2348
2102 def git(self, cmd, cwd=None): 2349 def commit(self, *, cwd=None):
2103 if isinstance(cmd, str): 2350 cwd = cwd or self.srcdir
2104 cmd = 'git ' + cmd 2351 self.git(["commit", "-m", "Change"], cwd=cwd)
2105 else: 2352 return self.git(["rev-parse", "HEAD"], cwd=cwd).strip()
2106 cmd = ['git'] + cmd 2353
2107 if cwd is None: 2354 def commit_file(self, filename, content, *, cwd=None):
2108 cwd = self.gitdir 2355 cwd = cwd or self.srcdir
2109 return bb.process.run(cmd, cwd=cwd)[0] 2356
2357 with open(os.path.join(cwd, filename), "w") as f:
2358 f.write(content)
2359 self.git(["add", filename], cwd=cwd)
2360 return self.commit(cwd=cwd)
2110 2361
2111 def fetch(self, uri=None, download=True): 2362 def fetch(self, uri=None, download=True):
2112 uris = self.d.getVar('SRC_URI').split() 2363 uris = self.d.getVar('SRC_URI').split()
@@ -2119,65 +2370,259 @@ class GitLfsTest(FetcherTest):
2119 ud = fetcher.ud[uri] 2370 ud = fetcher.ud[uri]
2120 return fetcher, ud 2371 return fetcher, ud
2121 2372
2373 def get_real_git_lfs_file(self):
2374 self.d.setVar('PATH', os.environ.get('PATH'))
2375 fetcher, ud = self.fetch()
2376 fetcher.unpack(self.d.getVar('WORKDIR'))
2377 unpacked_lfs_file = os.path.join(self.d.getVar('WORKDIR'), 'git', "Cat_poster_1.jpg")
2378 return unpacked_lfs_file
2379
2380 @skipIfNoGitLFS()
2381 def test_gitsm_lfs(self):
2382 """Test that the gitsm fetcher caches objects stored via LFS"""
2383 self.git(["lfs", "install", "--local"], cwd=self.srcdir)
2384
2385 def fetch_and_verify(revision, filename, content):
2386 self.d.setVar('SRCREV', revision)
2387 fetcher, ud = self.fetch()
2388
2389 with hide_directory(submoduledir), hide_directory(self.srcdir):
2390 workdir = self.d.getVar('WORKDIR')
2391 fetcher.unpack(workdir)
2392
2393 with open(os.path.join(workdir, "git", filename)) as f:
2394 self.assertEqual(f.read(), content)
2395
2396 # Create the git repository that will later be used as a submodule
2397 submoduledir = self.tempdir + "/submodule"
2398 bb.utils.mkdirhier(submoduledir)
2399 self.git_init(submoduledir)
2400 self.git(["lfs", "install", "--local"], cwd=submoduledir)
2401 self.commit_file('.gitattributes', '*.mp3 filter=lfs -text', cwd=submoduledir)
2402
2403 submodule_commit_1 = self.commit_file("a.mp3", "submodule version 1", cwd=submoduledir)
2404 _ = self.commit_file("a.mp3", "submodule version 2", cwd=submoduledir)
2405
2406 # Add the submodule to the repository at its current HEAD revision
2407 self.git(["-c", "protocol.file.allow=always", "submodule", "add", submoduledir, "submodule"],
2408 cwd=self.srcdir)
2409 base_commit_1 = self.commit()
2410
2411 # Let the submodule point at a different revision
2412 self.git(["checkout", submodule_commit_1], self.srcdir + "/submodule")
2413 self.git(["add", "submodule"], cwd=self.srcdir)
2414 base_commit_2 = self.commit()
2415
2416 # Add a LFS file to the repository
2417 base_commit_3 = self.commit_file("a.mp3", "version 1")
2418 # Update the added LFS file
2419 base_commit_4 = self.commit_file("a.mp3", "version 2")
2420
2421 self.d.setVar('SRC_URI', "gitsm://%s;protocol=file;lfs=1;branch=master" % self.srcdir)
2422
2423 # Verify that LFS objects referenced from submodules are fetched and checked out
2424 fetch_and_verify(base_commit_1, "submodule/a.mp3", "submodule version 2")
2425 # Verify that the repository inside the download cache of a submodile is extended with any
2426 # additional LFS objects needed when checking out a different revision.
2427 fetch_and_verify(base_commit_2, "submodule/a.mp3", "submodule version 1")
2428 # Verify that LFS objects referenced from the base repository are fetched and checked out
2429 fetch_and_verify(base_commit_3, "a.mp3", "version 1")
2430 # Verify that the cached repository is extended with any additional LFS objects required
2431 # when checking out a different revision.
2432 fetch_and_verify(base_commit_4, "a.mp3", "version 2")
2433
2434 @skipIfNoGitLFS()
2435 def test_gitsm_lfs_disabled(self):
2436 """Test that the gitsm fetcher does not use LFS when explicitly disabled"""
2437 self.git(["lfs", "install", "--local"], cwd=self.srcdir)
2438
2439 def fetch_and_verify(revision, filename, content):
2440 self.d.setVar('SRCREV', revision)
2441 fetcher, ud = self.fetch()
2442
2443 with hide_directory(submoduledir), hide_directory(self.srcdir):
2444 workdir = self.d.getVar('WORKDIR')
2445 fetcher.unpack(workdir)
2446
2447 with open(os.path.join(workdir, "git", filename)) as f:
2448 # Assume that LFS did not perform smudging when the expected content is
2449 # missing.
2450 self.assertNotEqual(f.read(), content)
2451
2452 # Create the git repository that will later be used as a submodule
2453 submoduledir = self.tempdir + "/submodule"
2454 bb.utils.mkdirhier(submoduledir)
2455 self.git_init(submoduledir)
2456 self.git(["lfs", "install", "--local"], cwd=submoduledir)
2457 self.commit_file('.gitattributes', '*.mp3 filter=lfs -text', cwd=submoduledir)
2458
2459 submodule_commit_1 = self.commit_file("a.mp3", "submodule version 1", cwd=submoduledir)
2460
2461 # Add the submodule to the repository at its current HEAD revision
2462 self.git(["-c", "protocol.file.allow=always", "submodule", "add", submoduledir, "submodule"],
2463 cwd=self.srcdir)
2464 base_commit_1 = self.commit()
2465
2466 # Add a LFS file to the repository
2467 base_commit_2 = self.commit_file("a.mp3", "version 1")
2468
2469 self.d.setVar('SRC_URI', "gitsm://%s;protocol=file;lfs=1;branch=master;lfs=0" % self.srcdir)
2470
2471 # Verify that LFS objects referenced from submodules are not fetched nor checked out
2472 fetch_and_verify(base_commit_1, "submodule/a.mp3", "submodule version 1")
2473 # Verify that the LFS objects referenced from the base repository are not fetched nor
2474 # checked out
2475 fetch_and_verify(base_commit_2, "a.mp3", "version 1")
2476
2477 @skipIfNoGitLFS()
2478 def test_fetch_lfs_on_srcrev_change(self):
2479 """Test if fetch downloads missing LFS objects when a different revision within an existing repository is requested"""
2480 self.git(["lfs", "install", "--local"], cwd=self.srcdir)
2481
2482 def fetch_and_verify(revision, filename, content):
2483 self.d.setVar('SRCREV', revision)
2484 fetcher, ud = self.fetch()
2485
2486 with hide_directory(self.srcdir):
2487 workdir = self.d.getVar('WORKDIR')
2488 fetcher.unpack(workdir)
2489
2490 with open(os.path.join(workdir, "git", filename)) as f:
2491 self.assertEqual(f.read(), content)
2492
2493 commit_1 = self.commit_file("a.mp3", "version 1")
2494 commit_2 = self.commit_file("a.mp3", "version 2")
2495
2496 self.d.setVar('SRC_URI', "git://%s;protocol=file;lfs=1;branch=master" % self.srcdir)
2497
2498 # Seed the local download folder by fetching the latest commit and verifying that the LFS contents are
2499 # available even when the upstream repository disappears.
2500 fetch_and_verify(commit_2, "a.mp3", "version 2")
2501 # Verify that even when an older revision is fetched, the needed LFS objects are fetched into the download
2502 # folder.
2503 fetch_and_verify(commit_1, "a.mp3", "version 1")
2504
2505 @skipIfNoGitLFS()
2506 @skipIfNoNetwork()
2507 def test_real_git_lfs_repo_succeeds_without_lfs_param(self):
2508 self.d.setVar('SRC_URI', "git://gitlab.com/gitlab-examples/lfs.git;protocol=https;branch=master")
2509 f = self.get_real_git_lfs_file()
2510 self.assertTrue(os.path.exists(f))
2511 self.assertEqual("c0baab607a97839c9a328b4310713307", bb.utils.md5_file(f))
2512
2513 @skipIfNoGitLFS()
2514 @skipIfNoNetwork()
2515 def test_real_git_lfs_repo_succeeds(self):
2516 self.d.setVar('SRC_URI', "git://gitlab.com/gitlab-examples/lfs.git;protocol=https;branch=master;lfs=1")
2517 f = self.get_real_git_lfs_file()
2518 self.assertTrue(os.path.exists(f))
2519 self.assertEqual("c0baab607a97839c9a328b4310713307", bb.utils.md5_file(f))
2520
2521 @skipIfNoGitLFS()
2522 @skipIfNoNetwork()
2523 def test_real_git_lfs_repo_skips(self):
2524 self.d.setVar('SRC_URI', "git://gitlab.com/gitlab-examples/lfs.git;protocol=https;branch=master;lfs=0")
2525 f = self.get_real_git_lfs_file()
2526 # This is the actual non-smudged placeholder file on the repo if git-lfs does not run
2527 lfs_file = (
2528 'version https://git-lfs.github.com/spec/v1\n'
2529 'oid sha256:34be66b1a39a1955b46a12588df9d5f6fc1da790e05cf01f3c7422f4bbbdc26b\n'
2530 'size 11423554\n'
2531 )
2532
2533 with open(f) as fh:
2534 self.assertEqual(lfs_file, fh.read())
2535
2536 @skipIfNoGitLFS()
2122 def test_lfs_enabled(self): 2537 def test_lfs_enabled(self):
2123 import shutil 2538 uri = 'git://%s;protocol=file;lfs=1;branch=master' % self.srcdir
2539 self.d.setVar('SRC_URI', uri)
2540
2541 # With git-lfs installed, test that we can fetch and unpack
2542 fetcher, ud = self.fetch()
2543 shutil.rmtree(self.gitdir, ignore_errors=True)
2544 fetcher.unpack(self.d.getVar('WORKDIR'))
2124 2545
2125 uri = 'git://%s;protocol=file;subdir=${S};lfs=1' % self.srcdir 2546 @skipIfNoGitLFS()
2547 def test_lfs_disabled(self):
2548 uri = 'git://%s;protocol=file;lfs=0;branch=master' % self.srcdir
2126 self.d.setVar('SRC_URI', uri) 2549 self.d.setVar('SRC_URI', uri)
2127 2550
2128 # Careful: suppress initial attempt at downloading until 2551 # Verify that the fetcher can survive even if the source
2129 # we know whether git-lfs is installed. 2552 # repository has Git LFS usage configured.
2130 fetcher, ud = self.fetch(uri=None, download=False) 2553 fetcher, ud = self.fetch()
2131 self.assertIsNotNone(ud.method._find_git_lfs) 2554 fetcher.unpack(self.d.getVar('WORKDIR'))
2132 2555
2133 # If git-lfs can be found, the unpack should be successful. Only 2556 @skipIfNoGitLFS()
2134 # attempt this with the real live copy of git-lfs installed. 2557 def test_lfs_enabled_not_installed_during_unpack(self):
2135 if ud.method._find_git_lfs(self.d): 2558 uri = 'git://%s;protocol=file;lfs=1;branch=master' % self.srcdir
2136 fetcher.download() 2559 self.d.setVar('SRC_URI', uri)
2137 shutil.rmtree(self.gitdir, ignore_errors=True)
2138 fetcher.unpack(self.d.getVar('WORKDIR'))
2139 2560
2561 # Careful: suppress initial attempt at downloading
2562 fetcher, ud = self.fetch(uri=None, download=False)
2563
2564 fetcher.download()
2140 # If git-lfs cannot be found, the unpack should throw an error 2565 # If git-lfs cannot be found, the unpack should throw an error
2141 with self.assertRaises(bb.fetch2.FetchError): 2566 with self.assertRaises(bb.fetch2.FetchError):
2567 with unittest.mock.patch("shutil.which", return_value=None):
2568 shutil.rmtree(self.gitdir, ignore_errors=True)
2569 fetcher.unpack(self.d.getVar('WORKDIR'))
2570
2571 def test_lfs_enabled_not_installed(self):
2572 uri = 'git://%s;protocol=file;lfs=1;branch=master' % self.srcdir
2573 self.d.setVar('SRC_URI', uri)
2574
2575 # Careful: suppress initial attempt at downloading
2576 fetcher, ud = self.fetch(uri=None, download=False)
2577
2578 # If git-lfs cannot be found, the download should throw an error
2579 with unittest.mock.patch("shutil.which", return_value=None):
2580 with self.assertRaises(bb.fetch2.FetchError):
2581 fetcher.download()
2582
2583 def test_lfs_disabled_not_installed(self):
2584 uri = 'git://%s;protocol=file;lfs=0;branch=master' % self.srcdir
2585 self.d.setVar('SRC_URI', uri)
2586
2587 # Careful: suppress initial attempt at downloading
2588 fetcher, ud = self.fetch(uri=None, download=False)
2589
2590 # Even if git-lfs cannot be found, the download / unpack should be successful
2591 with unittest.mock.patch("shutil.which", return_value=None):
2142 fetcher.download() 2592 fetcher.download()
2143 ud.method._find_git_lfs = lambda d: False
2144 shutil.rmtree(self.gitdir, ignore_errors=True) 2593 shutil.rmtree(self.gitdir, ignore_errors=True)
2145 fetcher.unpack(self.d.getVar('WORKDIR')) 2594 fetcher.unpack(self.d.getVar('WORKDIR'))
2146 2595
2147 def test_lfs_disabled(self): 2596 def test_lfs_enabled_not_installed_but_not_needed(self):
2148 import shutil 2597 srcdir = os.path.join(self.tempdir, "emptygit")
2598 bb.utils.mkdirhier(srcdir)
2599 self.git_init(srcdir)
2600 self.commit_file("test", "test content", cwd=srcdir)
2149 2601
2150 uri = 'git://%s;protocol=file;subdir=${S};lfs=0' % self.srcdir 2602 uri = 'git://%s;protocol=file;lfs=1;branch=master' % srcdir
2151 self.d.setVar('SRC_URI', uri) 2603 self.d.setVar('SRC_URI', uri)
2152 2604
2153 # In contrast to test_lfs_enabled(), allow the implicit download 2605 # Careful: suppress initial attempt at downloading
2154 # done by self.fetch() to occur here. The point of this test case 2606 fetcher, ud = self.fetch(uri=None, download=False)
2155 # is to verify that the fetcher can survive even if the source
2156 # repository has Git LFS usage configured.
2157 fetcher, ud = self.fetch()
2158 self.assertIsNotNone(ud.method._find_git_lfs)
2159
2160 # If git-lfs can be found, the unpack should be successful. A
2161 # live copy of git-lfs is not required for this case, so
2162 # unconditionally forge its presence.
2163 ud.method._find_git_lfs = lambda d: True
2164 shutil.rmtree(self.gitdir, ignore_errors=True)
2165 fetcher.unpack(self.d.getVar('WORKDIR'))
2166 2607
2167 # If git-lfs cannot be found, the unpack should be successful 2608 # It shouldnt't matter that git-lfs cannot be found as the repository configuration does not
2168 ud.method._find_git_lfs = lambda d: False 2609 # specify any LFS filters.
2169 shutil.rmtree(self.gitdir, ignore_errors=True) 2610 with unittest.mock.patch("shutil.which", return_value=None):
2170 fetcher.unpack(self.d.getVar('WORKDIR')) 2611 fetcher.download()
2612 shutil.rmtree(self.gitdir, ignore_errors=True)
2613 fetcher.unpack(self.d.getVar('WORKDIR'))
2171 2614
2172class GitURLWithSpacesTest(FetcherTest): 2615class GitURLWithSpacesTest(FetcherTest):
2173 test_git_urls = { 2616 test_git_urls = {
2174 "git://tfs-example.org:22/tfs/example%20path/example.git" : { 2617 "git://tfs-example.org:22/tfs/example%20path/example.git;branch=master" : {
2175 'url': 'git://tfs-example.org:22/tfs/example%20path/example.git', 2618 'url': 'git://tfs-example.org:22/tfs/example%20path/example.git;branch=master',
2619 'repo_url': 'git://tfs-example.org:22/tfs/example%20path/example.git',
2176 'gitsrcname': 'tfs-example.org.22.tfs.example_path.example.git', 2620 'gitsrcname': 'tfs-example.org.22.tfs.example_path.example.git',
2177 'path': '/tfs/example path/example.git' 2621 'path': '/tfs/example path/example.git'
2178 }, 2622 },
2179 "git://tfs-example.org:22/tfs/example%20path/example%20repo.git" : { 2623 "git://tfs-example.org:22/tfs/example%20path/example%20repo.git;branch=master" : {
2180 'url': 'git://tfs-example.org:22/tfs/example%20path/example%20repo.git', 2624 'url': 'git://tfs-example.org:22/tfs/example%20path/example%20repo.git;branch=master',
2625 'repo_url': 'git://tfs-example.org:22/tfs/example%20path/example%20repo.git',
2181 'gitsrcname': 'tfs-example.org.22.tfs.example_path.example_repo.git', 2626 'gitsrcname': 'tfs-example.org.22.tfs.example_path.example_repo.git',
2182 'path': '/tfs/example path/example repo.git' 2627 'path': '/tfs/example path/example repo.git'
2183 } 2628 }
@@ -2200,19 +2645,181 @@ class GitURLWithSpacesTest(FetcherTest):
2200 self.assertEqual(ud.lockfile, os.path.join(self.dldir, "git2", ref['gitsrcname'] + '.lock')) 2645 self.assertEqual(ud.lockfile, os.path.join(self.dldir, "git2", ref['gitsrcname'] + '.lock'))
2201 self.assertEqual(ud.clonedir, os.path.join(self.dldir, "git2", ref['gitsrcname'])) 2646 self.assertEqual(ud.clonedir, os.path.join(self.dldir, "git2", ref['gitsrcname']))
2202 self.assertEqual(ud.fullmirror, os.path.join(self.dldir, "git2_" + ref['gitsrcname'] + '.tar.gz')) 2647 self.assertEqual(ud.fullmirror, os.path.join(self.dldir, "git2_" + ref['gitsrcname'] + '.tar.gz'))
2648 self.assertEqual(ud.method._get_repo_url(ud), ref['repo_url'])
2649
2650
2651class FetchLocallyMissingTagFromRemote(FetcherTest):
2652 def setUp(self):
2653 FetcherTest.setUp(self)
2654 self.gitdir = os.path.join(self.tempdir, 'git')
2655 self.srcdir = os.path.join(self.tempdir, 'gitsource')
2656
2657 bb.utils.mkdirhier(self.srcdir)
2658 self.git_init(cwd=self.srcdir)
2659 self.d.setVar('WORKDIR', self.tempdir)
2660 self.d.setVar('S', self.gitdir)
2661
2662 uri = 'git://%s;protocol=file;subdir=${S};branch=master' % self.srcdir
2663 self.d.setVar('SRC_URI', uri)
2664
2665 open(os.path.join(self.srcdir, 'dummyfile'), 'w').close()
2666 self.git(['add', 'dummyfile'], self.srcdir)
2667 self.git(['commit', '-m', 'dummymsg', 'dummyfile'], self.srcdir)
2668
2669 def _fetch_and_unpack(self, uri_to_fetch):
2670 fetcher = bb.fetch2.Fetch([uri_to_fetch], self.d)
2671 fetcher.download()
2672 fetcher.unpack(self.d.getVar('WORKDIR'))
2673
2674 def test_tag_present_in_remote_but_not_local(self):
2675 # fetch a repo that has no tag in it
2676 # then add a tag to this repo, and fetch it again, without
2677 # changing SRC_REV, but by adding ';tag=tag1` to SRC_URI
2678 # the new tag should be fetched and unpacked
2679 srcrev = self.git('rev-parse HEAD', cwd=self.srcdir).strip()
2680 self.d.setVar('SRCREV', srcrev)
2681 src_uri = self.d.getVar('SRC_URI')
2682 self._fetch_and_unpack(src_uri)
2683
2684 self.git('tag -m -a tag1', cwd=self.srcdir)
2685
2686 src_uri = '%s;tag=tag1' % self.d.getVar('SRC_URI').split()[0]
2687 self.d.setVar('SRC_URI', src_uri)
2688 self._fetch_and_unpack(src_uri)
2689
2690 output = self.git('log --pretty=oneline -n 1 refs/tags/tag1', cwd=self.gitdir)
2691 assert "fatal: ambiguous argument" not in output
2692
2693
2694class CrateTest(FetcherTest):
2695 @skipIfNoNetwork()
2696 def test_crate_url(self):
2697
2698 uri = "crate://crates.io/glob/0.2.11"
2699 self.d.setVar('SRC_URI', uri)
2700
2701 uris = self.d.getVar('SRC_URI').split()
2702 d = self.d
2703
2704 fetcher = bb.fetch2.Fetch(uris, self.d)
2705 ud = fetcher.ud[fetcher.urls[0]]
2706
2707 self.assertIn("name", ud.parm)
2708 self.assertEqual(ud.parm["name"], "glob-0.2.11")
2709 self.assertIn("downloadfilename", ud.parm)
2710 self.assertEqual(ud.parm["downloadfilename"], "glob-0.2.11.crate")
2711
2712 fetcher.download()
2713 fetcher.unpack(self.tempdir)
2714 self.assertEqual(sorted(os.listdir(self.tempdir)), ['cargo_home', 'download' , 'unpacked'])
2715 self.assertEqual(sorted(os.listdir(self.tempdir + "/download")), ['glob-0.2.11.crate', 'glob-0.2.11.crate.done'])
2716 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/glob-0.2.11/.cargo-checksum.json"))
2717 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/glob-0.2.11/src/lib.rs"))
2718
2719 @skipIfNoNetwork()
2720 def test_crate_url_matching_recipe(self):
2721
2722 self.d.setVar('BP', 'glob-0.2.11')
2723
2724 uri = "crate://crates.io/glob/0.2.11"
2725 self.d.setVar('SRC_URI', uri)
2726
2727 uris = self.d.getVar('SRC_URI').split()
2728 d = self.d
2729
2730 fetcher = bb.fetch2.Fetch(uris, self.d)
2731 ud = fetcher.ud[fetcher.urls[0]]
2732
2733 self.assertIn("name", ud.parm)
2734 self.assertEqual(ud.parm["name"], "glob-0.2.11")
2735 self.assertIn("downloadfilename", ud.parm)
2736 self.assertEqual(ud.parm["downloadfilename"], "glob-0.2.11.crate")
2737
2738 fetcher.download()
2739 fetcher.unpack(self.tempdir)
2740 self.assertEqual(sorted(os.listdir(self.tempdir)), ['download', 'glob-0.2.11', 'unpacked'])
2741 self.assertEqual(sorted(os.listdir(self.tempdir + "/download")), ['glob-0.2.11.crate', 'glob-0.2.11.crate.done'])
2742 self.assertTrue(os.path.exists(self.tempdir + "/glob-0.2.11/src/lib.rs"))
2743
2744 @skipIfNoNetwork()
2745 def test_crate_url_params(self):
2746
2747 uri = "crate://crates.io/aho-corasick/0.7.20;name=aho-corasick-renamed"
2748 self.d.setVar('SRC_URI', uri)
2749
2750 uris = self.d.getVar('SRC_URI').split()
2751 d = self.d
2752
2753 fetcher = bb.fetch2.Fetch(uris, self.d)
2754 ud = fetcher.ud[fetcher.urls[0]]
2755
2756 self.assertIn("name", ud.parm)
2757 self.assertEqual(ud.parm["name"], "aho-corasick-renamed")
2758 self.assertIn("downloadfilename", ud.parm)
2759 self.assertEqual(ud.parm["downloadfilename"], "aho-corasick-0.7.20.crate")
2760
2761 fetcher.download()
2762 fetcher.unpack(self.tempdir)
2763 self.assertEqual(sorted(os.listdir(self.tempdir)), ['cargo_home', 'download' , 'unpacked'])
2764 self.assertEqual(sorted(os.listdir(self.tempdir + "/download")), ['aho-corasick-0.7.20.crate', 'aho-corasick-0.7.20.crate.done'])
2765 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/aho-corasick-0.7.20/.cargo-checksum.json"))
2766 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/aho-corasick-0.7.20/src/lib.rs"))
2767
2768 @skipIfNoNetwork()
2769 def test_crate_url_multi(self):
2770
2771 uri = "crate://crates.io/glob/0.2.11 crate://crates.io/time/0.1.35"
2772 self.d.setVar('SRC_URI', uri)
2773
2774 uris = self.d.getVar('SRC_URI').split()
2775 d = self.d
2776
2777 fetcher = bb.fetch2.Fetch(uris, self.d)
2778 ud = fetcher.ud[fetcher.urls[0]]
2779
2780 self.assertIn("name", ud.parm)
2781 self.assertEqual(ud.parm["name"], "glob-0.2.11")
2782 self.assertIn("downloadfilename", ud.parm)
2783 self.assertEqual(ud.parm["downloadfilename"], "glob-0.2.11.crate")
2784
2785 ud = fetcher.ud[fetcher.urls[1]]
2786 self.assertIn("name", ud.parm)
2787 self.assertEqual(ud.parm["name"], "time-0.1.35")
2788 self.assertIn("downloadfilename", ud.parm)
2789 self.assertEqual(ud.parm["downloadfilename"], "time-0.1.35.crate")
2790
2791 fetcher.download()
2792 fetcher.unpack(self.tempdir)
2793 self.assertEqual(sorted(os.listdir(self.tempdir)), ['cargo_home', 'download' , 'unpacked'])
2794 self.assertEqual(sorted(os.listdir(self.tempdir + "/download")), ['glob-0.2.11.crate', 'glob-0.2.11.crate.done', 'time-0.1.35.crate', 'time-0.1.35.crate.done'])
2795 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/glob-0.2.11/.cargo-checksum.json"))
2796 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/glob-0.2.11/src/lib.rs"))
2797 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/time-0.1.35/.cargo-checksum.json"))
2798 self.assertTrue(os.path.exists(self.tempdir + "/cargo_home/bitbake/time-0.1.35/src/lib.rs"))
2799
2800 @skipIfNoNetwork()
2801 def test_crate_incorrect_cksum(self):
2802 uri = "crate://crates.io/aho-corasick/0.7.20"
2803 self.d.setVar('SRC_URI', uri)
2804 self.d.setVarFlag("SRC_URI", "aho-corasick-0.7.20.sha256sum", hashlib.sha256("Invalid".encode("utf-8")).hexdigest())
2805
2806 uris = self.d.getVar('SRC_URI').split()
2807
2808 fetcher = bb.fetch2.Fetch(uris, self.d)
2809 with self.assertRaisesRegex(bb.fetch2.FetchError, "Fetcher failure for URL"):
2810 fetcher.download()
2203 2811
2204class NPMTest(FetcherTest): 2812class NPMTest(FetcherTest):
2205 def skipIfNoNpm(): 2813 def skipIfNoNpm():
2206 import shutil
2207 if not shutil.which('npm'): 2814 if not shutil.which('npm'):
2208 return unittest.skip('npm not installed, tests being skipped') 2815 return unittest.skip('npm not installed')
2209 return lambda f: f 2816 return lambda f: f
2210 2817
2211 @skipIfNoNpm() 2818 @skipIfNoNpm()
2212 @skipIfNoNetwork() 2819 @skipIfNoNetwork()
2213 def test_npm(self): 2820 def test_npm(self):
2214 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2821 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2215 fetcher = bb.fetch.Fetch([url], self.d) 2822 fetcher = bb.fetch.Fetch(urls, self.d)
2216 ud = fetcher.ud[fetcher.urls[0]] 2823 ud = fetcher.ud[fetcher.urls[0]]
2217 fetcher.download() 2824 fetcher.download()
2218 self.assertTrue(os.path.exists(ud.localpath)) 2825 self.assertTrue(os.path.exists(ud.localpath))
@@ -2225,9 +2832,9 @@ class NPMTest(FetcherTest):
2225 @skipIfNoNpm() 2832 @skipIfNoNpm()
2226 @skipIfNoNetwork() 2833 @skipIfNoNetwork()
2227 def test_npm_bad_checksum(self): 2834 def test_npm_bad_checksum(self):
2228 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2835 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2229 # Fetch once to get a tarball 2836 # Fetch once to get a tarball
2230 fetcher = bb.fetch.Fetch([url], self.d) 2837 fetcher = bb.fetch.Fetch(urls, self.d)
2231 ud = fetcher.ud[fetcher.urls[0]] 2838 ud = fetcher.ud[fetcher.urls[0]]
2232 fetcher.download() 2839 fetcher.download()
2233 self.assertTrue(os.path.exists(ud.localpath)) 2840 self.assertTrue(os.path.exists(ud.localpath))
@@ -2244,17 +2851,48 @@ class NPMTest(FetcherTest):
2244 @skipIfNoNpm() 2851 @skipIfNoNpm()
2245 @skipIfNoNetwork() 2852 @skipIfNoNetwork()
2246 def test_npm_premirrors(self): 2853 def test_npm_premirrors(self):
2247 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2854 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2248 # Fetch once to get a tarball 2855 # Fetch once to get a tarball
2249 fetcher = bb.fetch.Fetch([url], self.d) 2856 fetcher = bb.fetch.Fetch(urls, self.d)
2857 ud = fetcher.ud[fetcher.urls[0]]
2858 fetcher.download()
2859 self.assertTrue(os.path.exists(ud.localpath))
2860
2861 # Setup the mirror by renaming the download directory
2862 mirrordir = os.path.join(self.tempdir, 'mirror')
2863 bb.utils.rename(self.dldir, mirrordir)
2864 os.mkdir(self.dldir)
2865
2866 # Configure the premirror to be used
2867 self.d.setVar('PREMIRRORS', 'https?$://.*/.* file://%s/npm2' % mirrordir)
2868 self.d.setVar('BB_FETCH_PREMIRRORONLY', '1')
2869
2870 # Fetch again
2871 self.assertFalse(os.path.exists(ud.localpath))
2872 # The npm fetcher doesn't handle that the .resolved file disappears
2873 # while the fetcher object exists, which it does when we rename the
2874 # download directory to "mirror" above. Thus we need a new fetcher to go
2875 # with the now empty download directory.
2876 fetcher = bb.fetch.Fetch(urls, self.d)
2877 ud = fetcher.ud[fetcher.urls[0]]
2878 fetcher.download()
2879 self.assertTrue(os.path.exists(ud.localpath))
2880
2881 @skipIfNoNpm()
2882 @skipIfNoNetwork()
2883 def test_npm_premirrors_with_specified_filename(self):
2884 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2885 # Fetch once to get a tarball
2886 fetcher = bb.fetch.Fetch(urls, self.d)
2250 ud = fetcher.ud[fetcher.urls[0]] 2887 ud = fetcher.ud[fetcher.urls[0]]
2251 fetcher.download() 2888 fetcher.download()
2252 self.assertTrue(os.path.exists(ud.localpath)) 2889 self.assertTrue(os.path.exists(ud.localpath))
2253 # Setup the mirror 2890 # Setup the mirror
2254 mirrordir = os.path.join(self.tempdir, 'mirror') 2891 mirrordir = os.path.join(self.tempdir, 'mirror')
2255 bb.utils.mkdirhier(mirrordir) 2892 bb.utils.mkdirhier(mirrordir)
2256 os.replace(ud.localpath, os.path.join(mirrordir, os.path.basename(ud.localpath))) 2893 mirrorfilename = os.path.join(mirrordir, os.path.basename(ud.localpath))
2257 self.d.setVar('PREMIRRORS', 'https?$://.*/.* file://%s/\n' % mirrordir) 2894 os.replace(ud.localpath, mirrorfilename)
2895 self.d.setVar('PREMIRRORS', 'https?$://.*/.* file://%s' % mirrorfilename)
2258 self.d.setVar('BB_FETCH_PREMIRRORONLY', '1') 2896 self.d.setVar('BB_FETCH_PREMIRRORONLY', '1')
2259 # Fetch again 2897 # Fetch again
2260 self.assertFalse(os.path.exists(ud.localpath)) 2898 self.assertFalse(os.path.exists(ud.localpath))
@@ -2265,8 +2903,8 @@ class NPMTest(FetcherTest):
2265 @skipIfNoNetwork() 2903 @skipIfNoNetwork()
2266 def test_npm_mirrors(self): 2904 def test_npm_mirrors(self):
2267 # Fetch once to get a tarball 2905 # Fetch once to get a tarball
2268 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2906 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2269 fetcher = bb.fetch.Fetch([url], self.d) 2907 fetcher = bb.fetch.Fetch(urls, self.d)
2270 ud = fetcher.ud[fetcher.urls[0]] 2908 ud = fetcher.ud[fetcher.urls[0]]
2271 fetcher.download() 2909 fetcher.download()
2272 self.assertTrue(os.path.exists(ud.localpath)) 2910 self.assertTrue(os.path.exists(ud.localpath))
@@ -2274,7 +2912,7 @@ class NPMTest(FetcherTest):
2274 mirrordir = os.path.join(self.tempdir, 'mirror') 2912 mirrordir = os.path.join(self.tempdir, 'mirror')
2275 bb.utils.mkdirhier(mirrordir) 2913 bb.utils.mkdirhier(mirrordir)
2276 os.replace(ud.localpath, os.path.join(mirrordir, os.path.basename(ud.localpath))) 2914 os.replace(ud.localpath, os.path.join(mirrordir, os.path.basename(ud.localpath)))
2277 self.d.setVar('MIRRORS', 'https?$://.*/.* file://%s/\n' % mirrordir) 2915 self.d.setVar('MIRRORS', 'https?$://.*/.* file://%s/' % mirrordir)
2278 # Update the resolved url to an invalid url 2916 # Update the resolved url to an invalid url
2279 with open(ud.resolvefile, 'r') as f: 2917 with open(ud.resolvefile, 'r') as f:
2280 url = f.read() 2918 url = f.read()
@@ -2290,27 +2928,27 @@ class NPMTest(FetcherTest):
2290 @skipIfNoNpm() 2928 @skipIfNoNpm()
2291 @skipIfNoNetwork() 2929 @skipIfNoNetwork()
2292 def test_npm_destsuffix_downloadfilename(self): 2930 def test_npm_destsuffix_downloadfilename(self):
2293 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0;destsuffix=foo/bar;downloadfilename=foo-bar.tgz' 2931 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0;destsuffix=foo/bar;downloadfilename=foo-bar.tgz']
2294 fetcher = bb.fetch.Fetch([url], self.d) 2932 fetcher = bb.fetch.Fetch(urls, self.d)
2295 fetcher.download() 2933 fetcher.download()
2296 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'foo-bar.tgz'))) 2934 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'foo-bar.tgz')))
2297 fetcher.unpack(self.unpackdir) 2935 fetcher.unpack(self.unpackdir)
2298 unpackdir = os.path.join(self.unpackdir, 'foo', 'bar') 2936 unpackdir = os.path.join(self.unpackdir, 'foo', 'bar')
2299 self.assertTrue(os.path.exists(os.path.join(unpackdir, 'package.json'))) 2937 self.assertTrue(os.path.exists(os.path.join(unpackdir, 'package.json')))
2300 2938
2301 def test_npm_no_network_no_tarball(self): 2939 def test_npm_no_network_no_tarball(self):
2302 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2940 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2303 self.d.setVar('BB_NO_NETWORK', '1') 2941 self.d.setVar('BB_NO_NETWORK', '1')
2304 fetcher = bb.fetch.Fetch([url], self.d) 2942 fetcher = bb.fetch.Fetch(urls, self.d)
2305 with self.assertRaises(bb.fetch2.NetworkAccess): 2943 with self.assertRaises(bb.fetch2.NetworkAccess):
2306 fetcher.download() 2944 fetcher.download()
2307 2945
2308 @skipIfNoNpm() 2946 @skipIfNoNpm()
2309 @skipIfNoNetwork() 2947 @skipIfNoNetwork()
2310 def test_npm_no_network_with_tarball(self): 2948 def test_npm_no_network_with_tarball(self):
2311 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2949 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2312 # Fetch once to get a tarball 2950 # Fetch once to get a tarball
2313 fetcher = bb.fetch.Fetch([url], self.d) 2951 fetcher = bb.fetch.Fetch(urls, self.d)
2314 fetcher.download() 2952 fetcher.download()
2315 # Disable network access 2953 # Disable network access
2316 self.d.setVar('BB_NO_NETWORK', '1') 2954 self.d.setVar('BB_NO_NETWORK', '1')
@@ -2323,8 +2961,8 @@ class NPMTest(FetcherTest):
2323 @skipIfNoNpm() 2961 @skipIfNoNpm()
2324 @skipIfNoNetwork() 2962 @skipIfNoNetwork()
2325 def test_npm_registry_alternate(self): 2963 def test_npm_registry_alternate(self):
2326 url = 'npm://registry.freajs.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2964 urls = ['npm://skimdb.npmjs.com;package=@savoirfairelinux/node-server-example;version=1.0.0']
2327 fetcher = bb.fetch.Fetch([url], self.d) 2965 fetcher = bb.fetch.Fetch(urls, self.d)
2328 fetcher.download() 2966 fetcher.download()
2329 fetcher.unpack(self.unpackdir) 2967 fetcher.unpack(self.unpackdir)
2330 unpackdir = os.path.join(self.unpackdir, 'npm') 2968 unpackdir = os.path.join(self.unpackdir, 'npm')
@@ -2333,8 +2971,8 @@ class NPMTest(FetcherTest):
2333 @skipIfNoNpm() 2971 @skipIfNoNpm()
2334 @skipIfNoNetwork() 2972 @skipIfNoNetwork()
2335 def test_npm_version_latest(self): 2973 def test_npm_version_latest(self):
2336 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=latest' 2974 url = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=latest']
2337 fetcher = bb.fetch.Fetch([url], self.d) 2975 fetcher = bb.fetch.Fetch(url, self.d)
2338 fetcher.download() 2976 fetcher.download()
2339 fetcher.unpack(self.unpackdir) 2977 fetcher.unpack(self.unpackdir)
2340 unpackdir = os.path.join(self.unpackdir, 'npm') 2978 unpackdir = os.path.join(self.unpackdir, 'npm')
@@ -2343,46 +2981,46 @@ class NPMTest(FetcherTest):
2343 @skipIfNoNpm() 2981 @skipIfNoNpm()
2344 @skipIfNoNetwork() 2982 @skipIfNoNetwork()
2345 def test_npm_registry_invalid(self): 2983 def test_npm_registry_invalid(self):
2346 url = 'npm://registry.invalid.org;package=@savoirfairelinux/node-server-example;version=1.0.0' 2984 urls = ['npm://registry.invalid.org;package=@savoirfairelinux/node-server-example;version=1.0.0']
2347 fetcher = bb.fetch.Fetch([url], self.d) 2985 fetcher = bb.fetch.Fetch(urls, self.d)
2348 with self.assertRaises(bb.fetch2.FetchError): 2986 with self.assertRaises(bb.fetch2.FetchError):
2349 fetcher.download() 2987 fetcher.download()
2350 2988
2351 @skipIfNoNpm() 2989 @skipIfNoNpm()
2352 @skipIfNoNetwork() 2990 @skipIfNoNetwork()
2353 def test_npm_package_invalid(self): 2991 def test_npm_package_invalid(self):
2354 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/invalid;version=1.0.0' 2992 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/invalid;version=1.0.0']
2355 fetcher = bb.fetch.Fetch([url], self.d) 2993 fetcher = bb.fetch.Fetch(urls, self.d)
2356 with self.assertRaises(bb.fetch2.FetchError): 2994 with self.assertRaises(bb.fetch2.FetchError):
2357 fetcher.download() 2995 fetcher.download()
2358 2996
2359 @skipIfNoNpm() 2997 @skipIfNoNpm()
2360 @skipIfNoNetwork() 2998 @skipIfNoNetwork()
2361 def test_npm_version_invalid(self): 2999 def test_npm_version_invalid(self):
2362 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=invalid' 3000 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=invalid']
2363 with self.assertRaises(bb.fetch2.ParameterError): 3001 with self.assertRaises(bb.fetch2.ParameterError):
2364 fetcher = bb.fetch.Fetch([url], self.d) 3002 fetcher = bb.fetch.Fetch(urls, self.d)
2365 3003
2366 @skipIfNoNpm() 3004 @skipIfNoNpm()
2367 @skipIfNoNetwork() 3005 @skipIfNoNetwork()
2368 def test_npm_registry_none(self): 3006 def test_npm_registry_none(self):
2369 url = 'npm://;package=@savoirfairelinux/node-server-example;version=1.0.0' 3007 urls = ['npm://;package=@savoirfairelinux/node-server-example;version=1.0.0']
2370 with self.assertRaises(bb.fetch2.MalformedUrl): 3008 with self.assertRaises(bb.fetch2.MalformedUrl):
2371 fetcher = bb.fetch.Fetch([url], self.d) 3009 fetcher = bb.fetch.Fetch(urls, self.d)
2372 3010
2373 @skipIfNoNpm() 3011 @skipIfNoNpm()
2374 @skipIfNoNetwork() 3012 @skipIfNoNetwork()
2375 def test_npm_package_none(self): 3013 def test_npm_package_none(self):
2376 url = 'npm://registry.npmjs.org;version=1.0.0' 3014 urls = ['npm://registry.npmjs.org;version=1.0.0']
2377 with self.assertRaises(bb.fetch2.MissingParameterError): 3015 with self.assertRaises(bb.fetch2.MissingParameterError):
2378 fetcher = bb.fetch.Fetch([url], self.d) 3016 fetcher = bb.fetch.Fetch(urls, self.d)
2379 3017
2380 @skipIfNoNpm() 3018 @skipIfNoNpm()
2381 @skipIfNoNetwork() 3019 @skipIfNoNetwork()
2382 def test_npm_version_none(self): 3020 def test_npm_version_none(self):
2383 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example' 3021 urls = ['npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example']
2384 with self.assertRaises(bb.fetch2.MissingParameterError): 3022 with self.assertRaises(bb.fetch2.MissingParameterError):
2385 fetcher = bb.fetch.Fetch([url], self.d) 3023 fetcher = bb.fetch.Fetch(urls, self.d)
2386 3024
2387 def create_shrinkwrap_file(self, data): 3025 def create_shrinkwrap_file(self, data):
2388 import json 3026 import json
@@ -2391,32 +3029,30 @@ class NPMTest(FetcherTest):
2391 bb.utils.mkdirhier(datadir) 3029 bb.utils.mkdirhier(datadir)
2392 with open(swfile, 'w') as f: 3030 with open(swfile, 'w') as f:
2393 json.dump(data, f) 3031 json.dump(data, f)
2394 # Also configure the S directory
2395 self.sdir = os.path.join(self.unpackdir, 'S')
2396 self.d.setVar('S', self.sdir)
2397 return swfile 3032 return swfile
2398 3033
2399 @skipIfNoNpm()
2400 @skipIfNoNetwork() 3034 @skipIfNoNetwork()
2401 def test_npmsw(self): 3035 def test_npmsw(self):
2402 swfile = self.create_shrinkwrap_file({ 3036 swfile = self.create_shrinkwrap_file({
2403 'dependencies': { 3037 'packages': {
2404 'array-flatten': { 3038 'node_modules/array-flatten': {
2405 'version': '1.1.1', 3039 'version': '1.1.1',
2406 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3040 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2407 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=', 3041 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=',
2408 'dependencies': { 3042 'dependencies': {
2409 'content-type': { 3043 'content-type': "1.0.4"
2410 'version': 'https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz',
2411 'integrity': 'sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==',
2412 'dependencies': {
2413 'cookie': {
2414 'version': 'git+https://github.com/jshttp/cookie.git#aec1177c7da67e3b3273df96cf476824dbc9ae09',
2415 'from': 'git+https://github.com/jshttp/cookie.git'
2416 }
2417 }
2418 }
2419 } 3044 }
3045 },
3046 'node_modules/array-flatten/node_modules/content-type': {
3047 'version': '1.0.4',
3048 'resolved': 'https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz',
3049 'integrity': 'sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==',
3050 'dependencies': {
3051 'cookie': 'git+https://github.com/jshttp/cookie.git#aec1177c7da67e3b3273df96cf476824dbc9ae09'
3052 }
3053 },
3054 'node_modules/array-flatten/node_modules/content-type/node_modules/cookie': {
3055 'resolved': 'git+https://github.com/jshttp/cookie.git#aec1177c7da67e3b3273df96cf476824dbc9ae09'
2420 } 3056 }
2421 } 3057 }
2422 }) 3058 })
@@ -2426,22 +3062,34 @@ class NPMTest(FetcherTest):
2426 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'content-type-1.0.4.tgz'))) 3062 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'content-type-1.0.4.tgz')))
2427 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'git2', 'github.com.jshttp.cookie.git'))) 3063 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'git2', 'github.com.jshttp.cookie.git')))
2428 fetcher.unpack(self.unpackdir) 3064 fetcher.unpack(self.unpackdir)
2429 self.assertTrue(os.path.exists(os.path.join(self.sdir, 'npm-shrinkwrap.json'))) 3065 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'npm-shrinkwrap.json')))
2430 self.assertTrue(os.path.exists(os.path.join(self.sdir, 'node_modules', 'array-flatten', 'package.json'))) 3066 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'node_modules', 'array-flatten', 'package.json')))
2431 self.assertTrue(os.path.exists(os.path.join(self.sdir, 'node_modules', 'array-flatten', 'node_modules', 'content-type', 'package.json'))) 3067 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'node_modules', 'array-flatten', 'node_modules', 'content-type', 'package.json')))
2432 self.assertTrue(os.path.exists(os.path.join(self.sdir, 'node_modules', 'array-flatten', 'node_modules', 'content-type', 'node_modules', 'cookie', 'package.json'))) 3068 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'node_modules', 'array-flatten', 'node_modules', 'content-type', 'node_modules', 'cookie', 'package.json')))
3069
3070 @skipIfNoNetwork()
3071 def test_npmsw_git(self):
3072 swfile = self.create_shrinkwrap_file({
3073 'packages': {
3074 'node_modules/cookie': {
3075 'resolved': 'git+https://github.com/jshttp/cookie.git#aec1177c7da67e3b3273df96cf476824dbc9ae09'
3076 }
3077 }
3078 })
3079 fetcher = bb.fetch.Fetch(['npmsw://' + swfile], self.d)
3080 fetcher.download()
3081 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'git2', 'github.com.jshttp.cookie.git')))
2433 3082
2434 @skipIfNoNpm()
2435 @skipIfNoNetwork() 3083 @skipIfNoNetwork()
2436 def test_npmsw_dev(self): 3084 def test_npmsw_dev(self):
2437 swfile = self.create_shrinkwrap_file({ 3085 swfile = self.create_shrinkwrap_file({
2438 'dependencies': { 3086 'packages': {
2439 'array-flatten': { 3087 'node_modules/array-flatten': {
2440 'version': '1.1.1', 3088 'version': '1.1.1',
2441 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3089 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2442 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3090 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
2443 }, 3091 },
2444 'content-type': { 3092 'node_modules/content-type': {
2445 'version': '1.0.4', 3093 'version': '1.0.4',
2446 'resolved': 'https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz', 3094 'resolved': 'https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz',
2447 'integrity': 'sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==', 3095 'integrity': 'sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==',
@@ -2460,12 +3108,11 @@ class NPMTest(FetcherTest):
2460 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'array-flatten-1.1.1.tgz'))) 3108 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'array-flatten-1.1.1.tgz')))
2461 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'content-type-1.0.4.tgz'))) 3109 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'content-type-1.0.4.tgz')))
2462 3110
2463 @skipIfNoNpm()
2464 @skipIfNoNetwork() 3111 @skipIfNoNetwork()
2465 def test_npmsw_destsuffix(self): 3112 def test_npmsw_destsuffix(self):
2466 swfile = self.create_shrinkwrap_file({ 3113 swfile = self.create_shrinkwrap_file({
2467 'dependencies': { 3114 'packages': {
2468 'array-flatten': { 3115 'node_modules/array-flatten': {
2469 'version': '1.1.1', 3116 'version': '1.1.1',
2470 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3117 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2471 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3118 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
@@ -2479,8 +3126,8 @@ class NPMTest(FetcherTest):
2479 3126
2480 def test_npmsw_no_network_no_tarball(self): 3127 def test_npmsw_no_network_no_tarball(self):
2481 swfile = self.create_shrinkwrap_file({ 3128 swfile = self.create_shrinkwrap_file({
2482 'dependencies': { 3129 'packages': {
2483 'array-flatten': { 3130 'node_modules/array-flatten': {
2484 'version': '1.1.1', 3131 'version': '1.1.1',
2485 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3132 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2486 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3133 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
@@ -2502,8 +3149,8 @@ class NPMTest(FetcherTest):
2502 self.d.setVar('BB_NO_NETWORK', '1') 3149 self.d.setVar('BB_NO_NETWORK', '1')
2503 # Fetch again 3150 # Fetch again
2504 swfile = self.create_shrinkwrap_file({ 3151 swfile = self.create_shrinkwrap_file({
2505 'dependencies': { 3152 'packages': {
2506 'array-flatten': { 3153 'node_modules/array-flatten': {
2507 'version': '1.1.1', 3154 'version': '1.1.1',
2508 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3155 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2509 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3156 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
@@ -2513,15 +3160,14 @@ class NPMTest(FetcherTest):
2513 fetcher = bb.fetch.Fetch(['npmsw://' + swfile], self.d) 3160 fetcher = bb.fetch.Fetch(['npmsw://' + swfile], self.d)
2514 fetcher.download() 3161 fetcher.download()
2515 fetcher.unpack(self.unpackdir) 3162 fetcher.unpack(self.unpackdir)
2516 self.assertTrue(os.path.exists(os.path.join(self.sdir, 'node_modules', 'array-flatten', 'package.json'))) 3163 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'node_modules', 'array-flatten', 'package.json')))
2517 3164
2518 @skipIfNoNpm()
2519 @skipIfNoNetwork() 3165 @skipIfNoNetwork()
2520 def test_npmsw_npm_reusability(self): 3166 def test_npmsw_npm_reusability(self):
2521 # Fetch once with npmsw 3167 # Fetch once with npmsw
2522 swfile = self.create_shrinkwrap_file({ 3168 swfile = self.create_shrinkwrap_file({
2523 'dependencies': { 3169 'packages': {
2524 'array-flatten': { 3170 'node_modules/array-flatten': {
2525 'version': '1.1.1', 3171 'version': '1.1.1',
2526 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3172 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2527 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3173 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
@@ -2538,13 +3184,12 @@ class NPMTest(FetcherTest):
2538 fetcher.unpack(self.unpackdir) 3184 fetcher.unpack(self.unpackdir)
2539 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'npm', 'package.json'))) 3185 self.assertTrue(os.path.exists(os.path.join(self.unpackdir, 'npm', 'package.json')))
2540 3186
2541 @skipIfNoNpm()
2542 @skipIfNoNetwork() 3187 @skipIfNoNetwork()
2543 def test_npmsw_bad_checksum(self): 3188 def test_npmsw_bad_checksum(self):
2544 # Try to fetch with bad checksum 3189 # Try to fetch with bad checksum
2545 swfile = self.create_shrinkwrap_file({ 3190 swfile = self.create_shrinkwrap_file({
2546 'dependencies': { 3191 'packages': {
2547 'array-flatten': { 3192 'node_modules/array-flatten': {
2548 'version': '1.1.1', 3193 'version': '1.1.1',
2549 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3194 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2550 'integrity': 'sha1-gfNEp2hqgLTFKT6P3AsBYMgsBqg=' 3195 'integrity': 'sha1-gfNEp2hqgLTFKT6P3AsBYMgsBqg='
@@ -2556,8 +3201,8 @@ class NPMTest(FetcherTest):
2556 fetcher.download() 3201 fetcher.download()
2557 # Fetch correctly to get a tarball 3202 # Fetch correctly to get a tarball
2558 swfile = self.create_shrinkwrap_file({ 3203 swfile = self.create_shrinkwrap_file({
2559 'dependencies': { 3204 'packages': {
2560 'array-flatten': { 3205 'node_modules/array-flatten': {
2561 'version': '1.1.1', 3206 'version': '1.1.1',
2562 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3207 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2563 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3208 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
@@ -2590,13 +3235,13 @@ class NPMTest(FetcherTest):
2590 mirrordir = os.path.join(self.tempdir, 'mirror') 3235 mirrordir = os.path.join(self.tempdir, 'mirror')
2591 bb.utils.mkdirhier(mirrordir) 3236 bb.utils.mkdirhier(mirrordir)
2592 os.replace(ud.localpath, os.path.join(mirrordir, os.path.basename(ud.localpath))) 3237 os.replace(ud.localpath, os.path.join(mirrordir, os.path.basename(ud.localpath)))
2593 self.d.setVar('PREMIRRORS', 'https?$://.*/.* file://%s/\n' % mirrordir) 3238 self.d.setVar('PREMIRRORS', 'https?$://.*/.* file://%s/' % mirrordir)
2594 self.d.setVar('BB_FETCH_PREMIRRORONLY', '1') 3239 self.d.setVar('BB_FETCH_PREMIRRORONLY', '1')
2595 # Fetch again 3240 # Fetch again
2596 self.assertFalse(os.path.exists(ud.localpath)) 3241 self.assertFalse(os.path.exists(ud.localpath))
2597 swfile = self.create_shrinkwrap_file({ 3242 swfile = self.create_shrinkwrap_file({
2598 'dependencies': { 3243 'packages': {
2599 'array-flatten': { 3244 'node_modules/array-flatten': {
2600 'version': '1.1.1', 3245 'version': '1.1.1',
2601 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz', 3246 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
2602 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3247 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
@@ -2619,12 +3264,12 @@ class NPMTest(FetcherTest):
2619 mirrordir = os.path.join(self.tempdir, 'mirror') 3264 mirrordir = os.path.join(self.tempdir, 'mirror')
2620 bb.utils.mkdirhier(mirrordir) 3265 bb.utils.mkdirhier(mirrordir)
2621 os.replace(ud.localpath, os.path.join(mirrordir, os.path.basename(ud.localpath))) 3266 os.replace(ud.localpath, os.path.join(mirrordir, os.path.basename(ud.localpath)))
2622 self.d.setVar('MIRRORS', 'https?$://.*/.* file://%s/\n' % mirrordir) 3267 self.d.setVar('MIRRORS', 'https?$://.*/.* file://%s/' % mirrordir)
2623 # Fetch again with invalid url 3268 # Fetch again with invalid url
2624 self.assertFalse(os.path.exists(ud.localpath)) 3269 self.assertFalse(os.path.exists(ud.localpath))
2625 swfile = self.create_shrinkwrap_file({ 3270 swfile = self.create_shrinkwrap_file({
2626 'dependencies': { 3271 'packages': {
2627 'array-flatten': { 3272 'node_modules/array-flatten': {
2628 'version': '1.1.1', 3273 'version': '1.1.1',
2629 'resolved': 'https://invalid', 3274 'resolved': 'https://invalid',
2630 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=' 3275 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
@@ -2634,3 +3279,521 @@ class NPMTest(FetcherTest):
2634 fetcher = bb.fetch.Fetch(['npmsw://' + swfile], self.d) 3279 fetcher = bb.fetch.Fetch(['npmsw://' + swfile], self.d)
2635 fetcher.download() 3280 fetcher.download()
2636 self.assertTrue(os.path.exists(ud.localpath)) 3281 self.assertTrue(os.path.exists(ud.localpath))
3282
3283 @skipIfNoNetwork()
3284 def test_npmsw_bundled(self):
3285 swfile = self.create_shrinkwrap_file({
3286 'packages': {
3287 'node_modules/array-flatten': {
3288 'version': '1.1.1',
3289 'resolved': 'https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz',
3290 'integrity': 'sha1-ml9pkFGx5wczKPKgCJaLZOopVdI='
3291 },
3292 'node_modules/content-type': {
3293 'version': '1.0.4',
3294 'resolved': 'https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz',
3295 'integrity': 'sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==',
3296 'inBundle': True
3297 }
3298 }
3299 })
3300 fetcher = bb.fetch.Fetch(['npmsw://' + swfile], self.d)
3301 fetcher.download()
3302 self.assertTrue(os.path.exists(os.path.join(self.dldir, 'npm2', 'array-flatten-1.1.1.tgz')))
3303 self.assertFalse(os.path.exists(os.path.join(self.dldir, 'npm2', 'content-type-1.0.4.tgz')))
3304
3305class GitSharedTest(FetcherTest):
3306 def setUp(self):
3307 super(GitSharedTest, self).setUp()
3308 self.recipe_url = "git://git.openembedded.org/bitbake;branch=master;protocol=https"
3309 self.d.setVar('SRCREV', '82ea737a0b42a8b53e11c9cde141e9e9c0bd8c40')
3310 self.d.setVar("__BBSRCREV_SEEN", "1")
3311
3312 @skipIfNoNetwork()
3313 def test_shared_unpack(self):
3314 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3315
3316 fetcher.download()
3317 fetcher.unpack(self.unpackdir)
3318 alt = os.path.join(self.unpackdir, 'git/.git/objects/info/alternates')
3319 self.assertTrue(os.path.exists(alt))
3320
3321 @skipIfNoNetwork()
3322 def test_noshared_unpack(self):
3323 self.d.setVar('BB_GIT_NOSHARED', '1')
3324 self.unpackdir += '_noshared'
3325 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3326
3327 fetcher.download()
3328 fetcher.unpack(self.unpackdir)
3329 alt = os.path.join(self.unpackdir, 'git/.git/objects/info/alternates')
3330 self.assertFalse(os.path.exists(alt))
3331
3332class GitTagVerificationTests(FetcherTest):
3333
3334 @skipIfNoNetwork()
3335 def test_tag_rev_match(self):
3336 # Test a url with rev= and tag= set works
3337 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;rev=5b4e20377eea8d428edf1aeb2187c18f82ca6757;tag=2.12.0"], self.d)
3338 fetcher.download()
3339 fetcher.unpack(self.unpackdir)
3340
3341 def test_annotated_tag_rev_match(self):
3342 # Test a url with rev= and tag= set works
3343 # rev is the annotated tag revision in this case
3344 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;rev=fa30183549bd09f33fd4eebf56771ca5393526a6;tag=2.12.0"], self.d)
3345 fetcher.download()
3346 fetcher.unpack(self.unpackdir)
3347
3348 @skipIfNoNetwork()
3349 def test_tag_rev_match2(self):
3350 # Test a url with SRCREV and tag= set works
3351 self.d.setVar('SRCREV', '5b4e20377eea8d428edf1aeb2187c18f82ca6757')
3352 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;tag=2.12.0"], self.d)
3353 fetcher.download()
3354 fetcher.unpack(self.unpackdir)
3355
3356 @skipIfNoNetwork()
3357 def test_tag_rev_match3(self):
3358 # Test a url with SRCREV, rev= and tag= set works
3359 self.d.setVar('SRCREV', '5b4e20377eea8d428edf1aeb2187c18f82ca6757')
3360 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;rev=5b4e20377eea8d428edf1aeb2187c18f82ca6757;tag=2.12.0"], self.d)
3361 fetcher.download()
3362 fetcher.unpack(self.unpackdir)
3363
3364 @skipIfNoNetwork()
3365 def test_tag_rev_match4(self):
3366 # Test a url with SRCREV and rev= mismatching errors
3367 self.d.setVar('SRCREV', 'bade540fc31a1c26839efd2c7785a751ce24ebfb')
3368 with self.assertRaises(bb.fetch2.FetchError):
3369 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;rev=5b4e20377eea8d428edf1aeb2187c18f82ca6757;tag=2.12.0"], self.d)
3370
3371 @skipIfNoNetwork()
3372 def test_tag_rev_match5(self):
3373 # Test a url with SRCREV, rev= and tag= set works when using shallow clones
3374 self.d.setVar('BB_GIT_SHALLOW', '1')
3375 self.d.setVar('SRCREV', '5b4e20377eea8d428edf1aeb2187c18f82ca6757')
3376 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;rev=5b4e20377eea8d428edf1aeb2187c18f82ca6757;tag=2.12.0"], self.d)
3377 fetcher.download()
3378 fetcher.unpack(self.unpackdir)
3379
3380 @skipIfNoNetwork()
3381 def test_tag_rev_match6(self):
3382 # Test a url with SRCREV, rev= and a mismatched tag= when using shallow clones
3383 self.d.setVar('BB_GIT_SHALLOW', '1')
3384 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;rev=5b4e20377eea8d428edf1aeb2187c18f82ca6757;tag=2.8.0"], self.d)
3385 fetcher.download()
3386 with self.assertRaises(bb.fetch2.FetchError):
3387 fetcher.unpack(self.unpackdir)
3388
3389 @skipIfNoNetwork()
3390 def test_tag_rev_match7(self):
3391 # Test a url with SRCREV, rev= and a mismatched tag=
3392 self.d.setVar('SRCREV', '5b4e20377eea8d428edf1aeb2187c18f82ca6757')
3393 fetcher = bb.fetch.Fetch(["git://git.openembedded.org/bitbake;branch=2.12;protocol=https;rev=5b4e20377eea8d428edf1aeb2187c18f82ca6757;tag=2.8.0"], self.d)
3394 fetcher.download()
3395 with self.assertRaises(bb.fetch2.FetchError):
3396 fetcher.unpack(self.unpackdir)
3397
3398
3399class FetchPremirroronlyLocalTest(FetcherTest):
3400
3401 def setUp(self):
3402 super(FetchPremirroronlyLocalTest, self).setUp()
3403 self.mirrordir = os.path.join(self.tempdir, "mirrors")
3404 os.mkdir(self.mirrordir)
3405 self.reponame = "bitbake"
3406 self.gitdir = os.path.join(self.tempdir, "git", self.reponame)
3407 self.recipe_url = "git://git.fake.repo/bitbake;branch=master;protocol=https"
3408 self.d.setVar("BB_FETCH_PREMIRRORONLY", "1")
3409 self.d.setVar("BB_NO_NETWORK", "1")
3410 self.d.setVar("PREMIRRORS", self.recipe_url + " " + "file://{}".format(self.mirrordir) + " \n")
3411 self.mirrorname = "git2_git.fake.repo.bitbake.tar.gz"
3412 self.mirrorfile = os.path.join(self.mirrordir, self.mirrorname)
3413 self.testfilename = "bitbake-fetch.test"
3414
3415 def make_git_repo(self):
3416 recipeurl = "git:/git.fake.repo/bitbake"
3417 os.makedirs(self.gitdir)
3418 self.git_init(cwd=self.gitdir)
3419 for i in range(0):
3420 self.git_new_commit()
3421 bb.process.run('tar -czvf {} .'.format(os.path.join(self.mirrordir, self.mirrorname)), cwd = self.gitdir)
3422
3423 def git_new_commit(self):
3424 import random
3425 os.unlink(os.path.join(self.mirrordir, self.mirrorname))
3426 branch = self.git("branch --show-current", self.gitdir).split()
3427 with open(os.path.join(self.gitdir, self.testfilename), "w") as testfile:
3428 testfile.write("File {} from branch {}; Useless random data {}".format(self.testfilename, branch, random.random()))
3429 self.git("add {}".format(self.testfilename), self.gitdir)
3430 self.git("commit -a -m \"This random commit {} in branch {}. I'm useless.\"".format(random.random(), branch), self.gitdir)
3431 bb.process.run('tar -czvf {} .'.format(os.path.join(self.mirrordir, self.mirrorname)), cwd = self.gitdir)
3432 return self.git("rev-parse HEAD", self.gitdir).strip()
3433
3434 def git_new_branch(self, name):
3435 self.git_new_commit()
3436 head = self.git("rev-parse HEAD", self.gitdir).strip()
3437 self.git("checkout -b {}".format(name), self.gitdir)
3438 newrev = self.git_new_commit()
3439 self.git("checkout {}".format(head), self.gitdir)
3440 return newrev
3441
3442 def test_mirror_multiple_fetches(self):
3443 self.make_git_repo()
3444 self.d.setVar("SRCREV", self.git_new_commit())
3445 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3446 fetcher.download()
3447 fetcher.unpack(self.unpackdir)
3448 ## New commit in premirror. it's not in the download_dir
3449 self.d.setVar("SRCREV", self.git_new_commit())
3450 fetcher2 = bb.fetch.Fetch([self.recipe_url], self.d)
3451 fetcher2.download()
3452 fetcher2.unpack(self.unpackdir)
3453 ## New commit in premirror. it's not in the download_dir
3454 self.d.setVar("SRCREV", self.git_new_commit())
3455 fetcher3 = bb.fetch.Fetch([self.recipe_url], self.d)
3456 fetcher3.download()
3457 fetcher3.unpack(self.unpackdir)
3458
3459
3460 def test_mirror_commit_nonexistent(self):
3461 self.make_git_repo()
3462 self.d.setVar("SRCREV", "0"*40)
3463 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3464 with self.assertRaises(bb.fetch2.NetworkAccess):
3465 fetcher.download()
3466
3467 def test_mirror_commit_exists(self):
3468 self.make_git_repo()
3469 self.d.setVar("SRCREV", self.git_new_commit())
3470 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3471 fetcher.download()
3472 fetcher.unpack(self.unpackdir)
3473
3474 def test_mirror_tarball_nonexistent(self):
3475 self.d.setVar("SRCREV", "0"*40)
3476 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3477 with self.assertRaises(bb.fetch2.NetworkAccess):
3478 fetcher.download()
3479
3480
3481class FetchPremirroronlyNetworkTest(FetcherTest):
3482
3483 def setUp(self):
3484 super(FetchPremirroronlyNetworkTest, self).setUp()
3485 self.mirrordir = os.path.join(self.tempdir, "mirrors")
3486 os.mkdir(self.mirrordir)
3487 self.reponame = "fstests"
3488 self.clonedir = os.path.join(self.tempdir, "git")
3489 self.gitdir = os.path.join(self.tempdir, "git", "{}.git".format(self.reponame))
3490 self.recipe_url = "git://git.yoctoproject.org/fstests;protocol=https;branch=master"
3491 self.d.setVar("BB_FETCH_PREMIRRORONLY", "1")
3492 self.d.setVar("BB_NO_NETWORK", "0")
3493 self.d.setVar("PREMIRRORS", self.recipe_url + " " + "file://{}".format(self.mirrordir) + " \n")
3494
3495 def make_git_repo(self):
3496 self.mirrorname = "git2_git.yoctoproject.org.fstests.tar.gz"
3497 os.makedirs(self.clonedir)
3498 self.git("clone --bare {}".format(self.recipe_url), self.clonedir)
3499 self.git("update-ref HEAD 15413486df1f5a5b5af699b6f3ba5f0984e52a9f", self.gitdir)
3500 bb.process.run('tar -czvf {} .'.format(os.path.join(self.mirrordir, self.mirrorname)), cwd = self.gitdir)
3501 shutil.rmtree(self.clonedir)
3502
3503 @skipIfNoNetwork()
3504 def test_mirror_tarball_updated(self):
3505 self.make_git_repo()
3506 ## Upstream commit is in the mirror
3507 self.d.setVar("SRCREV", "15413486df1f5a5b5af699b6f3ba5f0984e52a9f")
3508 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3509 fetcher.download()
3510
3511 @skipIfNoNetwork()
3512 def test_mirror_tarball_outdated(self):
3513 self.make_git_repo()
3514 ## Upstream commit not in the mirror
3515 self.d.setVar("SRCREV", "49d65d53c2bf558ae6e9185af0f3af7b79d255ec")
3516 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3517 with self.assertRaises(bb.fetch2.NetworkAccess):
3518 fetcher.download()
3519
3520class FetchPremirroronlyMercurialTest(FetcherTest):
3521 """ Test for premirrors with mercurial repos
3522 the test covers also basic hg:// clone (see fetch_and_create_tarball
3523 """
3524 def skipIfNoHg():
3525 if not shutil.which('hg'):
3526 return unittest.skip('Mercurial not installed')
3527 return lambda f: f
3528
3529 def setUp(self):
3530 super(FetchPremirroronlyMercurialTest, self).setUp()
3531 self.mirrordir = os.path.join(self.tempdir, "mirrors")
3532 os.mkdir(self.mirrordir)
3533 self.reponame = "libgnt"
3534 self.clonedir = os.path.join(self.tempdir, "hg")
3535 self.recipe_url = "hg://keep.imfreedom.org/libgnt;module=libgnt"
3536 self.d.setVar("SRCREV", "53e8b422faaf")
3537 self.mirrorname = "hg_libgnt_keep.imfreedom.org_.libgnt.tar.gz"
3538
3539 def fetch_and_create_tarball(self):
3540 """
3541 Ask bitbake to download repo and prepare mirror tarball for us
3542 """
3543 self.d.setVar("BB_GENERATE_MIRROR_TARBALLS", "1")
3544 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3545 fetcher.download()
3546 mirrorfile = os.path.join(self.d.getVar("DL_DIR"), self.mirrorname)
3547 self.assertTrue(os.path.exists(mirrorfile), "Mirror tarball {} has not been created".format(mirrorfile))
3548 ## moving tarball to mirror directory
3549 os.rename(mirrorfile, os.path.join(self.mirrordir, self.mirrorname))
3550 self.d.setVar("BB_GENERATE_MIRROR_TARBALLS", "0")
3551
3552
3553 @skipIfNoNetwork()
3554 @skipIfNoHg()
3555 def test_premirror_mercurial(self):
3556 self.fetch_and_create_tarball()
3557 self.d.setVar("PREMIRRORS", self.recipe_url + " " + "file://{}".format(self.mirrordir) + " \n")
3558 self.d.setVar("BB_FETCH_PREMIRRORONLY", "1")
3559 self.d.setVar("BB_NO_NETWORK", "1")
3560 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3561 fetcher.download()
3562
3563class FetchPremirroronlyBrokenTarball(FetcherTest):
3564
3565 def setUp(self):
3566 super(FetchPremirroronlyBrokenTarball, self).setUp()
3567 self.mirrordir = os.path.join(self.tempdir, "mirrors")
3568 os.mkdir(self.mirrordir)
3569 self.reponame = "bitbake"
3570 self.gitdir = os.path.join(self.tempdir, "git", self.reponame)
3571 self.recipe_url = "git://git.fake.repo/bitbake;protocol=https;branch=master"
3572 self.d.setVar("BB_FETCH_PREMIRRORONLY", "1")
3573 self.d.setVar("BB_NO_NETWORK", "1")
3574 self.d.setVar("PREMIRRORS", self.recipe_url + " " + "file://{}".format(self.mirrordir) + " \n")
3575 self.mirrorname = "git2_git.fake.repo.bitbake.tar.gz"
3576 with open(os.path.join(self.mirrordir, self.mirrorname), 'w') as targz:
3577 targz.write("This is not tar.gz file!")
3578
3579 def test_mirror_broken_download(self):
3580 self.d.setVar("SRCREV", "0"*40)
3581 fetcher = bb.fetch.Fetch([self.recipe_url], self.d)
3582 with self.assertRaises(bb.fetch2.FetchError), self.assertLogs() as logs:
3583 fetcher.download()
3584 output = "".join(logs.output)
3585 self.assertFalse(" not a git repository (or any parent up to mount point /)" in output)
3586
3587class GoModTest(FetcherTest):
3588
3589 @skipIfNoNetwork()
3590 def test_gomod_url(self):
3591 urls = ['gomod://github.com/Azure/azure-sdk-for-go/sdk/storage/azblob;version=v1.0.0;'
3592 'sha256sum=9bb69aea32f1d59711701f9562d66432c9c0374205e5009d1d1a62f03fb4fdad']
3593
3594 fetcher = bb.fetch2.Fetch(urls, self.d)
3595 ud = fetcher.ud[urls[0]]
3596 self.assertEqual(ud.url, 'https://proxy.golang.org/github.com/%21azure/azure-sdk-for-go/sdk/storage/azblob/%40v/v1.0.0.zip')
3597 self.assertEqual(ud.parm['downloadfilename'], 'github.com.Azure.azure-sdk-for-go.sdk.storage.azblob@v1.0.0.zip')
3598 self.assertEqual(ud.parm['name'], 'github.com/Azure/azure-sdk-for-go/sdk/storage/azblob@v1.0.0')
3599
3600 fetcher.download()
3601 fetcher.unpack(self.unpackdir)
3602 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3603 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.zip')))
3604 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.mod')))
3605 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.mod')),
3606 '7873b8544842329b4f385a3aa6cf82cc2bc8defb41a04fa5291c35fd5900e873')
3607
3608 @skipIfNoNetwork()
3609 def test_gomod_url_go_mod_only(self):
3610 urls = ['gomod://github.com/Azure/azure-sdk-for-go/sdk/storage/azblob;version=v1.0.0;mod=1;'
3611 'sha256sum=7873b8544842329b4f385a3aa6cf82cc2bc8defb41a04fa5291c35fd5900e873']
3612
3613 fetcher = bb.fetch2.Fetch(urls, self.d)
3614 ud = fetcher.ud[urls[0]]
3615 self.assertEqual(ud.url, 'https://proxy.golang.org/github.com/%21azure/azure-sdk-for-go/sdk/storage/azblob/%40v/v1.0.0.mod')
3616 self.assertEqual(ud.parm['downloadfilename'], 'github.com.Azure.azure-sdk-for-go.sdk.storage.azblob@v1.0.0.mod')
3617 self.assertEqual(ud.parm['name'], 'github.com/Azure/azure-sdk-for-go/sdk/storage/azblob@v1.0.0')
3618
3619 fetcher.download()
3620 fetcher.unpack(self.unpackdir)
3621 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3622 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.mod')))
3623
3624 @skipIfNoNetwork()
3625 def test_gomod_url_sha256sum_varflag(self):
3626 urls = ['gomod://gopkg.in/ini.v1;version=v1.67.0']
3627 self.d.setVarFlag('SRC_URI', 'gopkg.in/ini.v1@v1.67.0.sha256sum', 'bd845dfc762a87a56e5a32a07770dc83e86976db7705d7f89c5dbafdc60b06c6')
3628
3629 fetcher = bb.fetch2.Fetch(urls, self.d)
3630 ud = fetcher.ud[urls[0]]
3631 self.assertEqual(ud.url, 'https://proxy.golang.org/gopkg.in/ini.v1/%40v/v1.67.0.zip')
3632 self.assertEqual(ud.parm['downloadfilename'], 'gopkg.in.ini.v1@v1.67.0.zip')
3633 self.assertEqual(ud.parm['name'], 'gopkg.in/ini.v1@v1.67.0')
3634
3635 fetcher.download()
3636 fetcher.unpack(self.unpackdir)
3637 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3638 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.zip')))
3639 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')))
3640 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')),
3641 '13aedd85db8e555104108e0e613bb7e4d1242af7f27c15423dd9ab63b60b72a1')
3642
3643 @skipIfNoNetwork()
3644 def test_gomod_url_no_go_mod_in_module(self):
3645 urls = ['gomod://gopkg.in/ini.v1;version=v1.67.0;'
3646 'sha256sum=bd845dfc762a87a56e5a32a07770dc83e86976db7705d7f89c5dbafdc60b06c6']
3647
3648 fetcher = bb.fetch2.Fetch(urls, self.d)
3649 ud = fetcher.ud[urls[0]]
3650 self.assertEqual(ud.url, 'https://proxy.golang.org/gopkg.in/ini.v1/%40v/v1.67.0.zip')
3651 self.assertEqual(ud.parm['downloadfilename'], 'gopkg.in.ini.v1@v1.67.0.zip')
3652 self.assertEqual(ud.parm['name'], 'gopkg.in/ini.v1@v1.67.0')
3653
3654 fetcher.download()
3655 fetcher.unpack(self.unpackdir)
3656 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3657 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.zip')))
3658 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')))
3659 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')),
3660 '13aedd85db8e555104108e0e613bb7e4d1242af7f27c15423dd9ab63b60b72a1')
3661
3662 @skipIfNoNetwork()
3663 def test_gomod_url_host_only(self):
3664 urls = ['gomod://go.opencensus.io;version=v0.24.0;'
3665 'sha256sum=203a767d7f8e7c1ebe5588220ad168d1e15b14ae70a636de7ca9a4a88a7e0d0c']
3666
3667 fetcher = bb.fetch2.Fetch(urls, self.d)
3668 ud = fetcher.ud[urls[0]]
3669 self.assertEqual(ud.url, 'https://proxy.golang.org/go.opencensus.io/%40v/v0.24.0.zip')
3670 self.assertEqual(ud.parm['downloadfilename'], 'go.opencensus.io@v0.24.0.zip')
3671 self.assertEqual(ud.parm['name'], 'go.opencensus.io@v0.24.0')
3672
3673 fetcher.download()
3674 fetcher.unpack(self.unpackdir)
3675 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3676 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'go.opencensus.io/@v/v0.24.0.zip')))
3677 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'go.opencensus.io/@v/v0.24.0.mod')))
3678 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'go.opencensus.io/@v/v0.24.0.mod')),
3679 '0dc9ccc660ad21cebaffd548f2cc6efa27891c68b4fbc1f8a3893b00f1acec96')
3680
3681class GoModGitTest(FetcherTest):
3682
3683 @skipIfNoNetwork()
3684 def test_gomodgit_url_repo(self):
3685 urls = ['gomodgit://golang.org/x/net;version=v0.9.0;'
3686 'repo=go.googlesource.com/net;'
3687 'srcrev=694cff8668bac64e0864b552bffc280cd27f21b1']
3688
3689 fetcher = bb.fetch2.Fetch(urls, self.d)
3690 ud = fetcher.ud[urls[0]]
3691 self.assertEqual(ud.host, 'go.googlesource.com')
3692 self.assertEqual(ud.path, '/net')
3693 self.assertEqual(ud.name, 'golang.org/x/net@v0.9.0')
3694 self.assertEqual(self.d.getVar('SRCREV_golang.org/x/net@v0.9.0'), '694cff8668bac64e0864b552bffc280cd27f21b1')
3695
3696 fetcher.download()
3697 self.assertTrue(os.path.exists(ud.localpath))
3698
3699 fetcher.unpack(self.unpackdir)
3700 vcsdir = os.path.join(self.unpackdir, 'pkg/mod/cache/vcs')
3701 self.assertTrue(os.path.exists(os.path.join(vcsdir, 'ed42bd05533fd84ae290a5d33ebd3695a0a2b06131beebd5450825bee8603aca')))
3702 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3703 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'golang.org/x/net/@v/v0.9.0.zip')))
3704 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'golang.org/x/net/@v/v0.9.0.mod')))
3705 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'golang.org/x/net/@v/v0.9.0.mod')),
3706 'c5d6851ede50ec1c001afb763040194b68961bf06997e2605e8bf06dcd2aeb2e')
3707
3708 @skipIfNoNetwork()
3709 def test_gomodgit_url_subdir(self):
3710 urls = ['gomodgit://github.com/Azure/azure-sdk-for-go/sdk/storage/azblob;version=v1.0.0;'
3711 'repo=github.com/Azure/azure-sdk-for-go;subdir=sdk/storage/azblob;'
3712 'srcrev=ec928e0ed34db682b3f783d3739d1c538142e0c3']
3713
3714 fetcher = bb.fetch2.Fetch(urls, self.d)
3715 ud = fetcher.ud[urls[0]]
3716 self.assertEqual(ud.host, 'github.com')
3717 self.assertEqual(ud.path, '/Azure/azure-sdk-for-go')
3718 self.assertEqual(ud.parm['subpath'], 'sdk/storage/azblob')
3719 self.assertEqual(ud.name, 'github.com/Azure/azure-sdk-for-go/sdk/storage/azblob@v1.0.0')
3720 self.assertEqual(self.d.getVar('SRCREV_github.com/Azure/azure-sdk-for-go/sdk/storage/azblob@v1.0.0'), 'ec928e0ed34db682b3f783d3739d1c538142e0c3')
3721
3722 fetcher.download()
3723 self.assertTrue(os.path.exists(ud.localpath))
3724
3725 fetcher.unpack(self.unpackdir)
3726 vcsdir = os.path.join(self.unpackdir, 'pkg/mod/cache/vcs')
3727 self.assertTrue(os.path.exists(os.path.join(vcsdir, 'd31d6145676ed3066ce573a8198f326dea5be45a43b3d8f41ce7787fd71d66b3')))
3728 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3729 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.zip')))
3730 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.mod')))
3731 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.mod')),
3732 '7873b8544842329b4f385a3aa6cf82cc2bc8defb41a04fa5291c35fd5900e873')
3733
3734 @skipIfNoNetwork()
3735 def test_gomodgit_url_srcrev_var(self):
3736 urls = ['gomodgit://gopkg.in/ini.v1;version=v1.67.0']
3737 self.d.setVar('SRCREV_gopkg.in/ini.v1@v1.67.0', 'b2f570e5b5b844226bbefe6fb521d891f529a951')
3738
3739 fetcher = bb.fetch2.Fetch(urls, self.d)
3740 ud = fetcher.ud[urls[0]]
3741 self.assertEqual(ud.host, 'gopkg.in')
3742 self.assertEqual(ud.path, '/ini.v1')
3743 self.assertEqual(ud.name, 'gopkg.in/ini.v1@v1.67.0')
3744 self.assertEqual(ud.parm['srcrev'], 'b2f570e5b5b844226bbefe6fb521d891f529a951')
3745
3746 fetcher.download()
3747 fetcher.unpack(self.unpackdir)
3748 vcsdir = os.path.join(self.unpackdir, 'pkg/mod/cache/vcs')
3749 self.assertTrue(os.path.exists(os.path.join(vcsdir, 'b7879a4be9ba8598851b8278b14c4f71a8316be64913298d1639cce6bde59bc3')))
3750 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3751 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.zip')))
3752 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')))
3753 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')),
3754 '13aedd85db8e555104108e0e613bb7e4d1242af7f27c15423dd9ab63b60b72a1')
3755
3756 @skipIfNoNetwork()
3757 def test_gomodgit_url_no_go_mod_in_module(self):
3758 urls = ['gomodgit://gopkg.in/ini.v1;version=v1.67.0;'
3759 'srcrev=b2f570e5b5b844226bbefe6fb521d891f529a951']
3760
3761 fetcher = bb.fetch2.Fetch(urls, self.d)
3762 ud = fetcher.ud[urls[0]]
3763 self.assertEqual(ud.host, 'gopkg.in')
3764 self.assertEqual(ud.path, '/ini.v1')
3765 self.assertEqual(ud.name, 'gopkg.in/ini.v1@v1.67.0')
3766 self.assertEqual(self.d.getVar('SRCREV_gopkg.in/ini.v1@v1.67.0'), 'b2f570e5b5b844226bbefe6fb521d891f529a951')
3767
3768 fetcher.download()
3769 fetcher.unpack(self.unpackdir)
3770 vcsdir = os.path.join(self.unpackdir, 'pkg/mod/cache/vcs')
3771 self.assertTrue(os.path.exists(os.path.join(vcsdir, 'b7879a4be9ba8598851b8278b14c4f71a8316be64913298d1639cce6bde59bc3')))
3772 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3773 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.zip')))
3774 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')))
3775 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')),
3776 '13aedd85db8e555104108e0e613bb7e4d1242af7f27c15423dd9ab63b60b72a1')
3777
3778 @skipIfNoNetwork()
3779 def test_gomodgit_url_host_only(self):
3780 urls = ['gomodgit://go.opencensus.io;version=v0.24.0;'
3781 'repo=github.com/census-instrumentation/opencensus-go;'
3782 'srcrev=b1a01ee95db0e690d91d7193d037447816fae4c5']
3783
3784 fetcher = bb.fetch2.Fetch(urls, self.d)
3785 ud = fetcher.ud[urls[0]]
3786 self.assertEqual(ud.host, 'github.com')
3787 self.assertEqual(ud.path, '/census-instrumentation/opencensus-go')
3788 self.assertEqual(ud.name, 'go.opencensus.io@v0.24.0')
3789 self.assertEqual(self.d.getVar('SRCREV_go.opencensus.io@v0.24.0'), 'b1a01ee95db0e690d91d7193d037447816fae4c5')
3790
3791 fetcher.download()
3792 fetcher.unpack(self.unpackdir)
3793 vcsdir = os.path.join(self.unpackdir, 'pkg/mod/cache/vcs')
3794 self.assertTrue(os.path.exists(os.path.join(vcsdir, 'aae3ac7b2122ed3345654e6327855e9682f4a5350d63e93dbcfc51c4419df0e1')))
3795 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3796 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'go.opencensus.io/@v/v0.24.0.zip')))
3797 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'go.opencensus.io/@v/v0.24.0.mod')))
3798 self.assertEqual(bb.utils.sha256_file(os.path.join(downloaddir, 'go.opencensus.io/@v/v0.24.0.mod')),
3799 '0dc9ccc660ad21cebaffd548f2cc6efa27891c68b4fbc1f8a3893b00f1acec96')
diff --git a/bitbake/lib/bb/tests/filter.py b/bitbake/lib/bb/tests/filter.py
new file mode 100644
index 0000000000..245df7b22b
--- /dev/null
+++ b/bitbake/lib/bb/tests/filter.py
@@ -0,0 +1,88 @@
1#
2# Copyright (C) 2025 Garmin Ltd. or its subsidiaries
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import unittest
8import bb.filter
9
10
11class BuiltinFilterTest(unittest.TestCase):
12 def test_disallowed_builtins(self):
13 with self.assertRaises(NameError):
14 val = bb.filter.apply_filters("1", ["open('foo.txt', 'rb')"])
15
16 def test_prefix(self):
17 val = bb.filter.apply_filters("1 2 3", ["prefix(val, 'a')"])
18 self.assertEqual(val, "a1 a2 a3")
19
20 val = bb.filter.apply_filters("", ["prefix(val, 'a')"])
21 self.assertEqual(val, "")
22
23 def test_suffix(self):
24 val = bb.filter.apply_filters("1 2 3", ["suffix(val, 'b')"])
25 self.assertEqual(val, "1b 2b 3b")
26
27 val = bb.filter.apply_filters("", ["suffix(val, 'b')"])
28 self.assertEqual(val, "")
29
30 def test_sort(self):
31 val = bb.filter.apply_filters("z y x", ["sort(val)"])
32 self.assertEqual(val, "x y z")
33
34 val = bb.filter.apply_filters("", ["sort(val)"])
35 self.assertEqual(val, "")
36
37 def test_identity(self):
38 val = bb.filter.apply_filters("1 2 3", ["val"])
39 self.assertEqual(val, "1 2 3")
40
41 val = bb.filter.apply_filters("123", ["val"])
42 self.assertEqual(val, "123")
43
44 def test_empty(self):
45 val = bb.filter.apply_filters("1 2 3", ["", "prefix(val, 'a')", ""])
46 self.assertEqual(val, "a1 a2 a3")
47
48 def test_nested(self):
49 val = bb.filter.apply_filters("1 2 3", ["prefix(prefix(val, 'a'), 'b')"])
50 self.assertEqual(val, "ba1 ba2 ba3")
51
52 val = bb.filter.apply_filters("1 2 3", ["prefix(prefix(val, 'b'), 'a')"])
53 self.assertEqual(val, "ab1 ab2 ab3")
54
55 def test_filter_order(self):
56 val = bb.filter.apply_filters("1 2 3", ["prefix(val, 'a')", "prefix(val, 'b')"])
57 self.assertEqual(val, "ba1 ba2 ba3")
58
59 val = bb.filter.apply_filters("1 2 3", ["prefix(val, 'b')", "prefix(val, 'a')"])
60 self.assertEqual(val, "ab1 ab2 ab3")
61
62 val = bb.filter.apply_filters("1 2 3", ["prefix(val, 'a')", "suffix(val, 'b')"])
63 self.assertEqual(val, "a1b a2b a3b")
64
65 val = bb.filter.apply_filters("1 2 3", ["suffix(val, 'b')", "prefix(val, 'a')"])
66 self.assertEqual(val, "a1b a2b a3b")
67
68 def test_remove(self):
69 val = bb.filter.apply_filters("1 2 3", ["remove(val, ['2'])"])
70 self.assertEqual(val, "1 3")
71
72 val = bb.filter.apply_filters("1,2,3", ["remove(val, ['2'], ',')"])
73 self.assertEqual(val, "1,3")
74
75 val = bb.filter.apply_filters("1 2 3", ["remove(val, ['4'])"])
76 self.assertEqual(val, "1 2 3")
77
78 val = bb.filter.apply_filters("1 2 3", ["remove(val, ['1', '2'])"])
79 self.assertEqual(val, "3")
80
81 val = bb.filter.apply_filters("1 2 3", ["remove(val, '2')"])
82 self.assertEqual(val, "1 3")
83
84 val = bb.filter.apply_filters("1 2 3", ["remove(val, '4')"])
85 self.assertEqual(val, "1 2 3")
86
87 val = bb.filter.apply_filters("1 2 3", ["remove(val, '1 2')"])
88 self.assertEqual(val, "3")
diff --git a/bitbake/lib/bb/tests/parse.py b/bitbake/lib/bb/tests/parse.py
index 9e21e18425..d3867ece98 100644
--- a/bitbake/lib/bb/tests/parse.py
+++ b/bitbake/lib/bb/tests/parse.py
@@ -39,7 +39,6 @@ C = "3"
39 os.chdir(self.origdir) 39 os.chdir(self.origdir)
40 40
41 def parsehelper(self, content, suffix = ".bb"): 41 def parsehelper(self, content, suffix = ".bb"):
42
43 f = tempfile.NamedTemporaryFile(suffix = suffix) 42 f = tempfile.NamedTemporaryFile(suffix = suffix)
44 f.write(bytes(content, "utf-8")) 43 f.write(bytes(content, "utf-8"))
45 f.flush() 44 f.flush()
@@ -47,17 +46,17 @@ C = "3"
47 return f 46 return f
48 47
49 def test_parse_simple(self): 48 def test_parse_simple(self):
50 f = self.parsehelper(self.testfile) 49 with self.parsehelper(self.testfile) as f:
51 d = bb.parse.handle(f.name, self.d)[''] 50 d = bb.parse.handle(f.name, self.d)['']
52 self.assertEqual(d.getVar("A"), "1") 51 self.assertEqual(d.getVar("A"), "1")
53 self.assertEqual(d.getVar("B"), "2") 52 self.assertEqual(d.getVar("B"), "2")
54 self.assertEqual(d.getVar("C"), "3") 53 self.assertEqual(d.getVar("C"), "3")
55 54
56 def test_parse_incomplete_function(self): 55 def test_parse_incomplete_function(self):
57 testfileB = self.testfile.replace("}", "") 56 testfileB = self.testfile.replace("}", "")
58 f = self.parsehelper(testfileB) 57 with self.parsehelper(testfileB) as f:
59 with self.assertRaises(bb.parse.ParseError): 58 with self.assertRaises(bb.parse.ParseError):
60 d = bb.parse.handle(f.name, self.d)[''] 59 d = bb.parse.handle(f.name, self.d)['']
61 60
62 unsettest = """ 61 unsettest = """
63A = "1" 62A = "1"
@@ -69,12 +68,65 @@ unset B[flag]
69""" 68"""
70 69
71 def test_parse_unset(self): 70 def test_parse_unset(self):
72 f = self.parsehelper(self.unsettest) 71 with self.parsehelper(self.unsettest) as f:
73 d = bb.parse.handle(f.name, self.d)[''] 72 d = bb.parse.handle(f.name, self.d)['']
74 self.assertEqual(d.getVar("A"), None) 73 self.assertEqual(d.getVar("A"), None)
75 self.assertEqual(d.getVarFlag("A","flag"), None) 74 self.assertEqual(d.getVarFlag("A","flag"), None)
76 self.assertEqual(d.getVar("B"), "2") 75 self.assertEqual(d.getVar("B"), "2")
77 76
77 defaulttest = """
78A = "set value"
79A ??= "default value"
80
81A[flag_set_vs_question] = "set flag"
82A[flag_set_vs_question] ?= "question flag"
83
84A[flag_set_vs_default] = "set flag"
85A[flag_set_vs_default] ??= "default flag"
86
87A[flag_question] ?= "question flag"
88
89A[flag_default] ??= "default flag"
90
91A[flag_question_vs_default] ?= "question flag"
92A[flag_question_vs_default] ??= "default flag"
93
94A[flag_default_vs_question] ??= "default flag"
95A[flag_default_vs_question] ?= "question flag"
96
97A[flag_set_question_default] = "set flag"
98A[flag_set_question_default] ?= "question flag"
99A[flag_set_question_default] ??= "default flag"
100
101A[flag_set_default_question] = "set flag"
102A[flag_set_default_question] ??= "default flag"
103A[flag_set_default_question] ?= "question flag"
104
105A[flag_set_twice] = "set flag first"
106A[flag_set_twice] = "set flag second"
107
108A[flag_question_twice] ?= "question flag first"
109A[flag_question_twice] ?= "question flag second"
110
111A[flag_default_twice] ??= "default flag first"
112A[flag_default_twice] ??= "default flag second"
113"""
114 def test_parse_defaulttest(self):
115 with self.parsehelper(self.defaulttest) as f:
116 d = bb.parse.handle(f.name, self.d)['']
117 self.assertEqual(d.getVar("A"), "set value")
118 self.assertEqual(d.getVarFlag("A","flag_set_vs_question"), "set flag")
119 self.assertEqual(d.getVarFlag("A","flag_set_vs_default"), "set flag")
120 self.assertEqual(d.getVarFlag("A","flag_question"), "question flag")
121 self.assertEqual(d.getVarFlag("A","flag_default"), "default flag")
122 self.assertEqual(d.getVarFlag("A","flag_question_vs_default"), "question flag")
123 self.assertEqual(d.getVarFlag("A","flag_default_vs_question"), "question flag")
124 self.assertEqual(d.getVarFlag("A","flag_set_question_default"), "set flag")
125 self.assertEqual(d.getVarFlag("A","flag_set_default_question"), "set flag")
126 self.assertEqual(d.getVarFlag("A","flag_set_twice"), "set flag second")
127 self.assertEqual(d.getVarFlag("A","flag_question_twice"), "question flag first")
128 self.assertEqual(d.getVarFlag("A","flag_default_twice"), "default flag second")
129
78 exporttest = """ 130 exporttest = """
79A = "a" 131A = "a"
80export B = "b" 132export B = "b"
@@ -83,8 +135,8 @@ exportD = "d"
83""" 135"""
84 136
85 def test_parse_exports(self): 137 def test_parse_exports(self):
86 f = self.parsehelper(self.exporttest) 138 with self.parsehelper(self.exporttest) as f:
87 d = bb.parse.handle(f.name, self.d)[''] 139 d = bb.parse.handle(f.name, self.d)['']
88 self.assertEqual(d.getVar("A"), "a") 140 self.assertEqual(d.getVar("A"), "a")
89 self.assertIsNone(d.getVarFlag("A", "export")) 141 self.assertIsNone(d.getVarFlag("A", "export"))
90 self.assertEqual(d.getVar("B"), "b") 142 self.assertEqual(d.getVar("B"), "b")
@@ -96,55 +148,53 @@ exportD = "d"
96 self.assertEqual(d.getVar("exportD"), "d") 148 self.assertEqual(d.getVar("exportD"), "d")
97 self.assertIsNone(d.getVarFlag("exportD", "export")) 149 self.assertIsNone(d.getVarFlag("exportD", "export"))
98 150
99
100 overridetest = """ 151 overridetest = """
101RRECOMMENDS_${PN} = "a" 152RRECOMMENDS:${PN} = "a"
102RRECOMMENDS_${PN}_libc = "b" 153RRECOMMENDS:${PN}:libc = "b"
103OVERRIDES = "libc:${PN}" 154OVERRIDES = "libc:${PN}"
104PN = "gtk+" 155PN = "gtk+"
105""" 156"""
106 157
107 def test_parse_overrides(self): 158 def test_parse_overrides(self):
108 f = self.parsehelper(self.overridetest) 159 with self.parsehelper(self.overridetest) as f:
109 d = bb.parse.handle(f.name, self.d)[''] 160 d = bb.parse.handle(f.name, self.d)['']
110 self.assertEqual(d.getVar("RRECOMMENDS"), "b") 161 self.assertEqual(d.getVar("RRECOMMENDS"), "b")
111 bb.data.expandKeys(d) 162 bb.data.expandKeys(d)
112 self.assertEqual(d.getVar("RRECOMMENDS"), "b") 163 self.assertEqual(d.getVar("RRECOMMENDS"), "b")
113 d.setVar("RRECOMMENDS_gtk+", "c") 164 d.setVar("RRECOMMENDS:gtk+", "c")
114 self.assertEqual(d.getVar("RRECOMMENDS"), "c") 165 self.assertEqual(d.getVar("RRECOMMENDS"), "c")
115 166
116 overridetest2 = """ 167 overridetest2 = """
117EXTRA_OECONF = "" 168EXTRA_OECONF = ""
118EXTRA_OECONF_class-target = "b" 169EXTRA_OECONF:class-target = "b"
119EXTRA_OECONF_append = " c" 170EXTRA_OECONF:append = " c"
120""" 171"""
121 172
122 def test_parse_overrides(self): 173 def test_parse_overrides2(self):
123 f = self.parsehelper(self.overridetest2) 174 with self.parsehelper(self.overridetest2) as f:
124 d = bb.parse.handle(f.name, self.d)[''] 175 d = bb.parse.handle(f.name, self.d)['']
125 d.appendVar("EXTRA_OECONF", " d") 176 d.appendVar("EXTRA_OECONF", " d")
126 d.setVar("OVERRIDES", "class-target") 177 d.setVar("OVERRIDES", "class-target")
127 self.assertEqual(d.getVar("EXTRA_OECONF"), "b c d") 178 self.assertEqual(d.getVar("EXTRA_OECONF"), "b c d")
128 179
129 overridetest3 = """ 180 overridetest3 = """
130DESCRIPTION = "A" 181DESCRIPTION = "A"
131DESCRIPTION_${PN}-dev = "${DESCRIPTION} B" 182DESCRIPTION:${PN}-dev = "${DESCRIPTION} B"
132PN = "bc" 183PN = "bc"
133""" 184"""
134 185
135 def test_parse_combinations(self): 186 def test_parse_combinations(self):
136 f = self.parsehelper(self.overridetest3) 187 with self.parsehelper(self.overridetest3) as f:
137 d = bb.parse.handle(f.name, self.d)[''] 188 d = bb.parse.handle(f.name, self.d)['']
138 bb.data.expandKeys(d) 189 bb.data.expandKeys(d)
139 self.assertEqual(d.getVar("DESCRIPTION_bc-dev"), "A B") 190 self.assertEqual(d.getVar("DESCRIPTION:bc-dev"), "A B")
140 d.setVar("DESCRIPTION", "E") 191 d.setVar("DESCRIPTION", "E")
141 d.setVar("DESCRIPTION_bc-dev", "C D") 192 d.setVar("DESCRIPTION:bc-dev", "C D")
142 d.setVar("OVERRIDES", "bc-dev") 193 d.setVar("OVERRIDES", "bc-dev")
143 self.assertEqual(d.getVar("DESCRIPTION"), "C D") 194 self.assertEqual(d.getVar("DESCRIPTION"), "C D")
144 195
145
146 classextend = """ 196 classextend = """
147VAR_var_override1 = "B" 197VAR_var:override1 = "B"
148EXTRA = ":override1" 198EXTRA = ":override1"
149OVERRIDES = "nothing${EXTRA}" 199OVERRIDES = "nothing${EXTRA}"
150 200
@@ -164,19 +214,32 @@ python () {
164 # become unset/disappear. 214 # become unset/disappear.
165 # 215 #
166 def test_parse_classextend_contamination(self): 216 def test_parse_classextend_contamination(self):
167 cls = self.parsehelper(self.classextend_bbclass, suffix=".bbclass") 217 self.d.setVar("__bbclasstype", "recipe")
168 #clsname = os.path.basename(cls.name).replace(".bbclass", "") 218 with self.parsehelper(self.classextend_bbclass, suffix=".bbclass") as cls:
169 self.classextend = self.classextend.replace("###CLASS###", cls.name) 219 #clsname = os.path.basename(cls.name).replace(".bbclass", "")
170 f = self.parsehelper(self.classextend) 220 self.classextend = self.classextend.replace("###CLASS###", cls.name)
171 alldata = bb.parse.handle(f.name, self.d) 221 with self.parsehelper(self.classextend) as f:
172 d1 = alldata[''] 222 alldata = bb.parse.handle(f.name, self.d)
173 d2 = alldata[cls.name] 223 d1 = alldata['']
174 self.assertEqual(d1.getVar("VAR_var"), "B") 224 d2 = alldata[cls.name]
175 self.assertEqual(d2.getVar("VAR_var"), None) 225 self.assertEqual(d1.getVar("VAR_var"), "B")
226 self.assertEqual(d2.getVar("VAR_var"), None)
176 227
177 addtask_deltask = """ 228 addtask_deltask = """
178addtask do_patch after do_foo after do_unpack before do_configure before do_compile 229addtask do_patch after do_foo after do_unpack before do_configure before do_compile
179addtask do_fetch do_patch 230addtask do_fetch2 do_patch2
231
232addtask do_myplaintask
233addtask do_myplaintask2
234deltask do_myplaintask2
235addtask do_mytask# comment
236addtask do_mytask2 # comment2
237addtask do_mytask3
238deltask do_mytask3# comment
239deltask do_mytask4 # comment2
240
241# Ensure a missing task prefix on after works
242addtask do_mytask5 after mytask
180 243
181MYVAR = "do_patch" 244MYVAR = "do_patch"
182EMPTYVAR = "" 245EMPTYVAR = ""
@@ -184,13 +247,264 @@ deltask do_fetch ${MYVAR} ${EMPTYVAR}
184deltask ${EMPTYVAR} 247deltask ${EMPTYVAR}
185""" 248"""
186 def test_parse_addtask_deltask(self): 249 def test_parse_addtask_deltask(self):
187 import sys 250 with self.parsehelper(self.addtask_deltask) as f:
188 f = self.parsehelper(self.addtask_deltask) 251 d = bb.parse.handle(f.name, self.d)['']
189 d = bb.parse.handle(f.name, self.d)[''] 252 self.assertSequenceEqual(['do_fetch2', 'do_patch2', 'do_myplaintask', 'do_mytask', 'do_mytask2', 'do_mytask5'], bb.build.listtasks(d))
253 self.assertEqual(['do_mytask'], d.getVarFlag("do_mytask5", "deps"))
254
255 broken_multiline_comment = """
256# First line of comment \\
257# Second line of comment \\
258
259"""
260 def test_parse_broken_multiline_comment(self):
261 with self.parsehelper(self.broken_multiline_comment) as f:
262 with self.assertRaises(bb.BBHandledException):
263 d = bb.parse.handle(f.name, self.d)['']
264
265 comment_in_var = """
266VAR = " \\
267 SOMEVAL \\
268# some comment \\
269 SOMEOTHERVAL \\
270"
271"""
272 def test_parse_comment_in_var(self):
273 with self.parsehelper(self.comment_in_var) as f:
274 with self.assertRaises(bb.BBHandledException):
275 d = bb.parse.handle(f.name, self.d)['']
276
277 at_sign_in_var_flag = """
278A[flag@.service] = "nonet"
279B[flag@.target] = "ntb"
280C[f] = "flag"
281
282unset A[flag@.service]
283"""
284 def test_parse_at_sign_in_var_flag(self):
285 with self.parsehelper(self.at_sign_in_var_flag) as f:
286 d = bb.parse.handle(f.name, self.d)['']
287 self.assertEqual(d.getVar("A"), None)
288 self.assertEqual(d.getVar("B"), None)
289 self.assertEqual(d.getVarFlag("A","flag@.service"), None)
290 self.assertEqual(d.getVarFlag("B","flag@.target"), "ntb")
291 self.assertEqual(d.getVarFlag("C","f"), "flag")
292
293 def test_parse_invalid_at_sign_in_var_flag(self):
294 invalid_at_sign = self.at_sign_in_var_flag.replace("B[f", "B[@f")
295 with self.parsehelper(invalid_at_sign) as f:
296 with self.assertRaises(bb.parse.ParseError):
297 d = bb.parse.handle(f.name, self.d)['']
298
299 export_function_recipe = """
300inherit someclass
301"""
302
303 export_function_recipe2 = """
304inherit someclass
305
306do_compile () {
307 false
308}
309
310python do_compilepython () {
311 bb.note("Something else")
312}
313
314"""
315 export_function_class = """
316someclass_do_compile() {
317 true
318}
319
320python someclass_do_compilepython () {
321 bb.note("Something")
322}
323
324EXPORT_FUNCTIONS do_compile do_compilepython
325"""
326
327 export_function_class2 = """
328secondclass_do_compile() {
329 true
330}
331
332python secondclass_do_compilepython () {
333 bb.note("Something")
334}
190 335
191 stdout = sys.stdout.getvalue() 336EXPORT_FUNCTIONS do_compile do_compilepython
192 self.assertTrue("addtask contained multiple 'before' keywords" in stdout) 337"""
193 self.assertTrue("addtask contained multiple 'after' keywords" in stdout) 338
194 self.assertTrue('addtask ignored: " do_patch"' in stdout) 339 def test_parse_export_functions(self):
195 #self.assertTrue('dependent task do_foo for do_patch does not exist' in stdout) 340 def check_function_flags(d):
341 self.assertEqual(d.getVarFlag("do_compile", "func"), 1)
342 self.assertEqual(d.getVarFlag("do_compilepython", "func"), 1)
343 self.assertEqual(d.getVarFlag("do_compile", "python"), None)
344 self.assertEqual(d.getVarFlag("do_compilepython", "python"), "1")
345
346 with tempfile.TemporaryDirectory() as tempdir:
347 self.d.setVar("__bbclasstype", "recipe")
348 recipename = tempdir + "/recipe.bb"
349 os.makedirs(tempdir + "/classes")
350 with open(tempdir + "/classes/someclass.bbclass", "w") as f:
351 f.write(self.export_function_class)
352 with open(tempdir + "/classes/secondclass.bbclass", "w") as f:
353 f.write(self.export_function_class2)
354
355 with open(recipename, "w") as f:
356 f.write(self.export_function_recipe)
357 os.chdir(tempdir)
358 d = bb.parse.handle(recipename, bb.data.createCopy(self.d))['']
359 self.assertIn("someclass_do_compile", d.getVar("do_compile"))
360 self.assertIn("someclass_do_compilepython", d.getVar("do_compilepython"))
361 check_function_flags(d)
362
363 recipename2 = tempdir + "/recipe2.bb"
364 with open(recipename2, "w") as f:
365 f.write(self.export_function_recipe2)
366
367 d = bb.parse.handle(recipename2, bb.data.createCopy(self.d))['']
368 self.assertNotIn("someclass_do_compile", d.getVar("do_compile"))
369 self.assertNotIn("someclass_do_compilepython", d.getVar("do_compilepython"))
370 self.assertIn("false", d.getVar("do_compile"))
371 self.assertIn("else", d.getVar("do_compilepython"))
372 check_function_flags(d)
373
374 with open(recipename, "a+") as f:
375 f.write("\ninherit secondclass\n")
376 with open(recipename2, "a+") as f:
377 f.write("\ninherit secondclass\n")
378
379 d = bb.parse.handle(recipename, bb.data.createCopy(self.d))['']
380 self.assertIn("secondclass_do_compile", d.getVar("do_compile"))
381 self.assertIn("secondclass_do_compilepython", d.getVar("do_compilepython"))
382 check_function_flags(d)
383
384 d = bb.parse.handle(recipename2, bb.data.createCopy(self.d))['']
385 self.assertNotIn("someclass_do_compile", d.getVar("do_compile"))
386 self.assertNotIn("someclass_do_compilepython", d.getVar("do_compilepython"))
387 self.assertIn("false", d.getVar("do_compile"))
388 self.assertIn("else", d.getVar("do_compilepython"))
389 check_function_flags(d)
390
391 export_function_unclosed_tab = """
392do_compile () {
393 bb.note("Something")
394\t}
395"""
396 export_function_unclosed_space = """
397do_compile () {
398 bb.note("Something")
399 }
400"""
401 export_function_residue = """
402do_compile () {
403 bb.note("Something")
404}
405
406include \\
407"""
408
409 def test_unclosed_functions(self):
410 def test_helper(content, expected_error):
411 with tempfile.TemporaryDirectory() as tempdir:
412 recipename = tempdir + "/recipe_unclosed.bb"
413 with open(recipename, "w") as f:
414 f.write(content)
415 os.chdir(tempdir)
416 with self.assertRaises(bb.parse.ParseError) as error:
417 bb.parse.handle(recipename, bb.data.createCopy(self.d))
418 self.assertIn(expected_error, str(error.exception))
419
420 with tempfile.TemporaryDirectory() as tempdir:
421 test_helper(self.export_function_unclosed_tab, "Unparsed lines from unclosed function")
422 test_helper(self.export_function_unclosed_space, "Unparsed lines from unclosed function")
423 test_helper(self.export_function_residue, "Unparsed lines")
424
425 recipename_closed = tempdir + "/recipe_closed.bb"
426 with open(recipename_closed, "w") as in_file:
427 lines = self.export_function_unclosed_tab.split("\n")
428 lines[3] = "}"
429 in_file.write("\n".join(lines))
430 bb.parse.handle(recipename_closed, bb.data.createCopy(self.d))
431
432 special_character_assignment = """
433A+="a"
434A+ = "b"
435+ = "c"
436"""
437 ambigous_assignment = """
438+= "d"
439"""
440 def test_parse_special_character_assignment(self):
441 with self.parsehelper(self.special_character_assignment) as f:
442 d = bb.parse.handle(f.name, self.d)['']
443 self.assertEqual(d.getVar("A"), " a")
444 self.assertEqual(d.getVar("A+"), "b")
445 self.assertEqual(d.getVar("+"), "c")
446
447 with self.parsehelper(self.ambigous_assignment) as f:
448 with self.assertRaises(bb.parse.ParseError) as error:
449 bb.parse.handle(f.name, self.d)
450 self.assertIn("Empty variable name in assignment", str(error.exception))
451
452 someconf1 = """
453EXTRA_OECONF:append = " foo"
454"""
455
456 someconf2 = """
457EXTRA_OECONF:append = " bar"
458"""
459
460 someconf3 = """
461EXTRA_OECONF:append = " foobar"
462"""
196 463
464 def test_include_and_require(self):
465 def test_helper(content, result):
466 with self.parsehelper(content) as f:
467 if isinstance(result, type) and issubclass(result, Exception):
468 with self.assertRaises(result):
469 d = bb.parse.handle(f.name, bb.data.createCopy(self.d))['']
470 else:
471 d = bb.parse.handle(f.name, bb.data.createCopy(self.d))['']
472 self.assertEqual(d.getVar("EXTRA_OECONF"), result)
473
474 with tempfile.TemporaryDirectory() as tempdir:
475 os.makedirs(tempdir + "/conf1")
476 os.makedirs(tempdir + "/conf2")
477
478 with open(tempdir + "/conf1/some.conf", "w") as f:
479 f.write(self.someconf1)
480 with open(tempdir + "/conf2/some.conf", "w") as f:
481 f.write(self.someconf2)
482 with open(tempdir + "/conf2/some3.conf", "w") as f:
483 f.write(self.someconf3)
484
485 self.d.setVar("BBPATH", tempdir + "/conf1" + ":" + tempdir + "/conf2")
486
487 test_helper("include some.conf", " foo")
488 test_helper("include someother.conf", None)
489 test_helper("include some3.conf", " foobar")
490 test_helper("include ${@''}", None)
491 test_helper("include " + tempdir + "/conf2/some.conf", " bar")
492
493 test_helper("require some.conf", " foo")
494 test_helper("require someother.conf", bb.parse.ParseError)
495 test_helper("require some3.conf", " foobar")
496 test_helper("require ${@''}", None)
497 test_helper("require " + tempdir + "/conf2/some.conf", " bar")
498
499 test_helper("include_all some.conf", " foo bar")
500 test_helper("include_all someother.conf", None)
501 test_helper("include_all some3.conf", " foobar")
502
503 self.d.setVar("BBPATH", tempdir + "/conf2" + ":" + tempdir + "/conf1")
504
505 test_helper("include some.conf", " bar")
506 test_helper("include some3.conf", " foobar")
507 test_helper("require some.conf", " bar")
508 test_helper("require some3.conf", " foobar")
509 test_helper("include_all some.conf", " bar foo")
510 test_helper("include_all some3.conf", " foobar")
diff --git a/bitbake/lib/bb/tests/persist_data.py b/bitbake/lib/bb/tests/persist_data.py
deleted file mode 100644
index f641b5acbc..0000000000
--- a/bitbake/lib/bb/tests/persist_data.py
+++ /dev/null
@@ -1,129 +0,0 @@
1#
2# BitBake Test for lib/bb/persist_data/
3#
4# Copyright (C) 2018 Garmin Ltd.
5#
6# SPDX-License-Identifier: GPL-2.0-only
7#
8
9import unittest
10import bb.data
11import bb.persist_data
12import tempfile
13import threading
14
15class PersistDataTest(unittest.TestCase):
16 def _create_data(self):
17 return bb.persist_data.persist('TEST_PERSIST_DATA', self.d)
18
19 def setUp(self):
20 self.d = bb.data.init()
21 self.tempdir = tempfile.TemporaryDirectory()
22 self.d['PERSISTENT_DIR'] = self.tempdir.name
23 self.data = self._create_data()
24 self.items = {
25 'A1': '1',
26 'B1': '2',
27 'C2': '3'
28 }
29 self.stress_count = 10000
30 self.thread_count = 5
31
32 for k,v in self.items.items():
33 self.data[k] = v
34
35 def tearDown(self):
36 self.tempdir.cleanup()
37
38 def _iter_helper(self, seen, iterator):
39 with iter(iterator):
40 for v in iterator:
41 self.assertTrue(v in seen)
42 seen.remove(v)
43 self.assertEqual(len(seen), 0, '%s not seen' % seen)
44
45 def test_get(self):
46 for k, v in self.items.items():
47 self.assertEqual(self.data[k], v)
48
49 self.assertIsNone(self.data.get('D'))
50 with self.assertRaises(KeyError):
51 self.data['D']
52
53 def test_set(self):
54 for k, v in self.items.items():
55 self.data[k] += '-foo'
56
57 for k, v in self.items.items():
58 self.assertEqual(self.data[k], v + '-foo')
59
60 def test_delete(self):
61 self.data['D'] = '4'
62 self.assertEqual(self.data['D'], '4')
63 del self.data['D']
64 self.assertIsNone(self.data.get('D'))
65 with self.assertRaises(KeyError):
66 self.data['D']
67
68 def test_contains(self):
69 for k in self.items:
70 self.assertTrue(k in self.data)
71 self.assertTrue(self.data.has_key(k))
72 self.assertFalse('NotFound' in self.data)
73 self.assertFalse(self.data.has_key('NotFound'))
74
75 def test_len(self):
76 self.assertEqual(len(self.data), len(self.items))
77
78 def test_iter(self):
79 self._iter_helper(set(self.items.keys()), self.data)
80
81 def test_itervalues(self):
82 self._iter_helper(set(self.items.values()), self.data.itervalues())
83
84 def test_iteritems(self):
85 self._iter_helper(set(self.items.items()), self.data.iteritems())
86
87 def test_get_by_pattern(self):
88 self._iter_helper({'1', '2'}, self.data.get_by_pattern('_1'))
89
90 def _stress_read(self, data):
91 for i in range(self.stress_count):
92 for k in self.items:
93 data[k]
94
95 def _stress_write(self, data):
96 for i in range(self.stress_count):
97 for k, v in self.items.items():
98 data[k] = v + str(i)
99
100 def _validate_stress(self):
101 for k, v in self.items.items():
102 self.assertEqual(self.data[k], v + str(self.stress_count - 1))
103
104 def test_stress(self):
105 self._stress_read(self.data)
106 self._stress_write(self.data)
107 self._validate_stress()
108
109 def test_stress_threads(self):
110 def read_thread():
111 data = self._create_data()
112 self._stress_read(data)
113
114 def write_thread():
115 data = self._create_data()
116 self._stress_write(data)
117
118 threads = []
119 for i in range(self.thread_count):
120 threads.append(threading.Thread(target=read_thread))
121 threads.append(threading.Thread(target=write_thread))
122
123 for t in threads:
124 t.start()
125 self._stress_read(self.data)
126 for t in threads:
127 t.join()
128 self._validate_stress()
129
diff --git a/bitbake/lib/bb/tests/runqueue-tests/classes/base.bbclass b/bitbake/lib/bb/tests/runqueue-tests/classes/base.bbclass
index b57650d591..3a3db55d2c 100644
--- a/bitbake/lib/bb/tests/runqueue-tests/classes/base.bbclass
+++ b/bitbake/lib/bb/tests/runqueue-tests/classes/base.bbclass
@@ -9,7 +9,7 @@ def stamptask(d):
9 with open(stampname, "a+") as f: 9 with open(stampname, "a+") as f:
10 f.write(d.getVar("BB_UNIHASH") + "\n") 10 f.write(d.getVar("BB_UNIHASH") + "\n")
11 11
12 if d.getVar("BB_CURRENT_MC") != "default": 12 if d.getVar("BB_CURRENT_MC") != "":
13 thistask = d.expand("${BB_CURRENT_MC}:${PN}:${BB_CURRENTTASK}") 13 thistask = d.expand("${BB_CURRENT_MC}:${PN}:${BB_CURRENTTASK}")
14 if thistask in d.getVar("SLOWTASKS").split(): 14 if thistask in d.getVar("SLOWTASKS").split():
15 bb.note("Slowing task %s" % thistask) 15 bb.note("Slowing task %s" % thistask)
@@ -98,7 +98,7 @@ do_package_qa[rdeptask] = "do_packagedata"
98do_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy" 98do_populate_lic_deploy[recrdeptask] += "do_populate_lic do_deploy"
99 99
100DEBIANRDEP = "do_packagedata" 100DEBIANRDEP = "do_packagedata"
101oo_package_write_ipk[rdeptask] = "${DEBIANRDEP}" 101do_package_write_ipk[rdeptask] = "${DEBIANRDEP}"
102do_package_write_rpm[rdeptask] = "${DEBIANRDEP}" 102do_package_write_rpm[rdeptask] = "${DEBIANRDEP}"
103 103
104addtask fetch 104addtask fetch
diff --git a/bitbake/lib/bb/tests/runqueue-tests/conf/bitbake.conf b/bitbake/lib/bb/tests/runqueue-tests/conf/bitbake.conf
index efebf001a9..05d7fd07dd 100644
--- a/bitbake/lib/bb/tests/runqueue-tests/conf/bitbake.conf
+++ b/bitbake/lib/bb/tests/runqueue-tests/conf/bitbake.conf
@@ -12,6 +12,6 @@ STAMP = "${TMPDIR}/stamps/${PN}"
12T = "${TMPDIR}/workdir/${PN}/temp" 12T = "${TMPDIR}/workdir/${PN}/temp"
13BB_NUMBER_THREADS = "4" 13BB_NUMBER_THREADS = "4"
14 14
15BB_HASHBASE_WHITELIST = "BB_CURRENT_MC BB_HASHSERVE TMPDIR TOPDIR SLOWTASKS SSTATEVALID FILE" 15BB_BASEHASH_IGNORE_VARS = "BB_CURRENT_MC BB_HASHSERVE TMPDIR TOPDIR SLOWTASKS SSTATEVALID FILE BB_CURRENTTASK"
16 16
17include conf/multiconfig/${BB_CURRENT_MC}.conf 17include conf/multiconfig/${BB_CURRENT_MC}.conf
diff --git a/bitbake/lib/bb/tests/runqueue-tests/recipes/g1.bb b/bitbake/lib/bb/tests/runqueue-tests/recipes/g1.bb
new file mode 100644
index 0000000000..3c7dca0257
--- /dev/null
+++ b/bitbake/lib/bb/tests/runqueue-tests/recipes/g1.bb
@@ -0,0 +1,2 @@
1do_build[mcdepends] = "mc::mc-1:h1:do_invalid"
2
diff --git a/bitbake/lib/bb/tests/runqueue-tests/recipes/h1.bb b/bitbake/lib/bb/tests/runqueue-tests/recipes/h1.bb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/bitbake/lib/bb/tests/runqueue-tests/recipes/h1.bb
diff --git a/bitbake/lib/bb/tests/runqueue.py b/bitbake/lib/bb/tests/runqueue.py
index 3d51779d6c..74f5ded2e6 100644
--- a/bitbake/lib/bb/tests/runqueue.py
+++ b/bitbake/lib/bb/tests/runqueue.py
@@ -26,20 +26,23 @@ class RunQueueTests(unittest.TestCase):
26 a1_sstatevalid = "a1:do_package a1:do_package_qa a1:do_packagedata a1:do_package_write_ipk a1:do_package_write_rpm a1:do_populate_lic a1:do_populate_sysroot" 26 a1_sstatevalid = "a1:do_package a1:do_package_qa a1:do_packagedata a1:do_package_write_ipk a1:do_package_write_rpm a1:do_populate_lic a1:do_populate_sysroot"
27 b1_sstatevalid = "b1:do_package b1:do_package_qa b1:do_packagedata b1:do_package_write_ipk b1:do_package_write_rpm b1:do_populate_lic b1:do_populate_sysroot" 27 b1_sstatevalid = "b1:do_package b1:do_package_qa b1:do_packagedata b1:do_package_write_ipk b1:do_package_write_rpm b1:do_populate_lic b1:do_populate_sysroot"
28 28
29 def run_bitbakecmd(self, cmd, builddir, sstatevalid="", slowtasks="", extraenv=None, cleanup=False): 29 def run_bitbakecmd(self, cmd, builddir, sstatevalid="", slowtasks="", extraenv=None, cleanup=False, allowfailure=False):
30 env = os.environ.copy() 30 env = os.environ.copy()
31 env["BBPATH"] = os.path.realpath(os.path.join(os.path.dirname(__file__), "runqueue-tests")) 31 env["BBPATH"] = os.path.realpath(os.path.join(os.path.dirname(__file__), "runqueue-tests"))
32 env["BB_ENV_EXTRAWHITE"] = "SSTATEVALID SLOWTASKS" 32 env["BB_ENV_PASSTHROUGH_ADDITIONS"] = "SSTATEVALID SLOWTASKS TOPDIR"
33 env["SSTATEVALID"] = sstatevalid 33 env["SSTATEVALID"] = sstatevalid
34 env["SLOWTASKS"] = slowtasks 34 env["SLOWTASKS"] = slowtasks
35 env["TOPDIR"] = builddir
35 if extraenv: 36 if extraenv:
36 for k in extraenv: 37 for k in extraenv:
37 env[k] = extraenv[k] 38 env[k] = extraenv[k]
38 env["BB_ENV_EXTRAWHITE"] = env["BB_ENV_EXTRAWHITE"] + " " + k 39 env["BB_ENV_PASSTHROUGH_ADDITIONS"] = env["BB_ENV_PASSTHROUGH_ADDITIONS"] + " " + k
39 try: 40 try:
40 output = subprocess.check_output(cmd, env=env, stderr=subprocess.STDOUT,universal_newlines=True, cwd=builddir) 41 output = subprocess.check_output(cmd, env=env, stderr=subprocess.STDOUT,universal_newlines=True, cwd=builddir)
41 print(output) 42 print(output)
42 except subprocess.CalledProcessError as e: 43 except subprocess.CalledProcessError as e:
44 if allowfailure:
45 return e.output
43 self.fail("Command %s failed with %s" % (cmd, e.output)) 46 self.fail("Command %s failed with %s" % (cmd, e.output))
44 tasks = [] 47 tasks = []
45 tasklog = builddir + "/task.log" 48 tasklog = builddir + "/task.log"
@@ -58,6 +61,8 @@ class RunQueueTests(unittest.TestCase):
58 expected = ['a1:' + x for x in self.alltasks] 61 expected = ['a1:' + x for x in self.alltasks]
59 self.assertEqual(set(tasks), set(expected)) 62 self.assertEqual(set(tasks), set(expected))
60 63
64 self.shutdown(tempdir)
65
61 def test_single_setscenevalid(self): 66 def test_single_setscenevalid(self):
62 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 67 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
63 cmd = ["bitbake", "a1"] 68 cmd = ["bitbake", "a1"]
@@ -68,6 +73,8 @@ class RunQueueTests(unittest.TestCase):
68 'a1:populate_sysroot', 'a1:build'] 73 'a1:populate_sysroot', 'a1:build']
69 self.assertEqual(set(tasks), set(expected)) 74 self.assertEqual(set(tasks), set(expected))
70 75
76 self.shutdown(tempdir)
77
71 def test_intermediate_setscenevalid(self): 78 def test_intermediate_setscenevalid(self):
72 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 79 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
73 cmd = ["bitbake", "a1"] 80 cmd = ["bitbake", "a1"]
@@ -77,6 +84,8 @@ class RunQueueTests(unittest.TestCase):
77 'a1:populate_sysroot_setscene', 'a1:build'] 84 'a1:populate_sysroot_setscene', 'a1:build']
78 self.assertEqual(set(tasks), set(expected)) 85 self.assertEqual(set(tasks), set(expected))
79 86
87 self.shutdown(tempdir)
88
80 def test_intermediate_notcovered(self): 89 def test_intermediate_notcovered(self):
81 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 90 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
82 cmd = ["bitbake", "a1"] 91 cmd = ["bitbake", "a1"]
@@ -86,6 +95,8 @@ class RunQueueTests(unittest.TestCase):
86 'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene'] 95 'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene']
87 self.assertEqual(set(tasks), set(expected)) 96 self.assertEqual(set(tasks), set(expected))
88 97
98 self.shutdown(tempdir)
99
89 def test_all_setscenevalid(self): 100 def test_all_setscenevalid(self):
90 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 101 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
91 cmd = ["bitbake", "a1"] 102 cmd = ["bitbake", "a1"]
@@ -95,6 +106,8 @@ class RunQueueTests(unittest.TestCase):
95 'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene'] 106 'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene']
96 self.assertEqual(set(tasks), set(expected)) 107 self.assertEqual(set(tasks), set(expected))
97 108
109 self.shutdown(tempdir)
110
98 def test_no_settasks(self): 111 def test_no_settasks(self):
99 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 112 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
100 cmd = ["bitbake", "a1", "-c", "patch"] 113 cmd = ["bitbake", "a1", "-c", "patch"]
@@ -103,6 +116,8 @@ class RunQueueTests(unittest.TestCase):
103 expected = ['a1:fetch', 'a1:unpack', 'a1:patch'] 116 expected = ['a1:fetch', 'a1:unpack', 'a1:patch']
104 self.assertEqual(set(tasks), set(expected)) 117 self.assertEqual(set(tasks), set(expected))
105 118
119 self.shutdown(tempdir)
120
106 def test_mix_covered_notcovered(self): 121 def test_mix_covered_notcovered(self):
107 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 122 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
108 cmd = ["bitbake", "a1:do_patch", "a1:do_populate_sysroot"] 123 cmd = ["bitbake", "a1:do_patch", "a1:do_populate_sysroot"]
@@ -111,6 +126,7 @@ class RunQueueTests(unittest.TestCase):
111 expected = ['a1:fetch', 'a1:unpack', 'a1:patch', 'a1:populate_sysroot_setscene'] 126 expected = ['a1:fetch', 'a1:unpack', 'a1:patch', 'a1:populate_sysroot_setscene']
112 self.assertEqual(set(tasks), set(expected)) 127 self.assertEqual(set(tasks), set(expected))
113 128
129 self.shutdown(tempdir)
114 130
115 # Test targets with intermediate setscene tasks alongside a target with no intermediate setscene tasks 131 # Test targets with intermediate setscene tasks alongside a target with no intermediate setscene tasks
116 def test_mixed_direct_tasks_setscene_tasks(self): 132 def test_mixed_direct_tasks_setscene_tasks(self):
@@ -122,6 +138,8 @@ class RunQueueTests(unittest.TestCase):
122 'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene'] 138 'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene']
123 self.assertEqual(set(tasks), set(expected)) 139 self.assertEqual(set(tasks), set(expected))
124 140
141 self.shutdown(tempdir)
142
125 # This test slows down the execution of do_package_setscene until after other real tasks have 143 # This test slows down the execution of do_package_setscene until after other real tasks have
126 # started running which tests for a bug where tasks were being lost from the buildable list of real 144 # started running which tests for a bug where tasks were being lost from the buildable list of real
127 # tasks if they weren't in tasks_covered or tasks_notcovered 145 # tasks if they weren't in tasks_covered or tasks_notcovered
@@ -136,12 +154,14 @@ class RunQueueTests(unittest.TestCase):
136 'a1:populate_sysroot', 'a1:build'] 154 'a1:populate_sysroot', 'a1:build']
137 self.assertEqual(set(tasks), set(expected)) 155 self.assertEqual(set(tasks), set(expected))
138 156
139 def test_setscenewhitelist(self): 157 self.shutdown(tempdir)
158
159 def test_setscene_ignore_tasks(self):
140 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 160 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
141 cmd = ["bitbake", "a1"] 161 cmd = ["bitbake", "a1"]
142 extraenv = { 162 extraenv = {
143 "BB_SETSCENE_ENFORCE" : "1", 163 "BB_SETSCENE_ENFORCE" : "1",
144 "BB_SETSCENE_ENFORCE_WHITELIST" : "a1:do_package_write_rpm a1:do_build" 164 "BB_SETSCENE_ENFORCE_IGNORE_TASKS" : "a1:do_package_write_rpm a1:do_build"
145 } 165 }
146 sstatevalid = "a1:do_package a1:do_package_qa a1:do_packagedata a1:do_package_write_ipk a1:do_populate_lic a1:do_populate_sysroot" 166 sstatevalid = "a1:do_package a1:do_package_qa a1:do_packagedata a1:do_package_write_ipk a1:do_populate_lic a1:do_populate_sysroot"
147 tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv) 167 tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv)
@@ -149,6 +169,8 @@ class RunQueueTests(unittest.TestCase):
149 'a1:populate_sysroot_setscene', 'a1:package_setscene'] 169 'a1:populate_sysroot_setscene', 'a1:package_setscene']
150 self.assertEqual(set(tasks), set(expected)) 170 self.assertEqual(set(tasks), set(expected))
151 171
172 self.shutdown(tempdir)
173
152 # Tests for problems with dependencies between setscene tasks 174 # Tests for problems with dependencies between setscene tasks
153 def test_no_setscenevalid_harddeps(self): 175 def test_no_setscenevalid_harddeps(self):
154 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 176 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
@@ -162,6 +184,8 @@ class RunQueueTests(unittest.TestCase):
162 'd1:populate_sysroot', 'd1:build'] 184 'd1:populate_sysroot', 'd1:build']
163 self.assertEqual(set(tasks), set(expected)) 185 self.assertEqual(set(tasks), set(expected))
164 186
187 self.shutdown(tempdir)
188
165 def test_no_setscenevalid_withdeps(self): 189 def test_no_setscenevalid_withdeps(self):
166 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 190 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
167 cmd = ["bitbake", "b1"] 191 cmd = ["bitbake", "b1"]
@@ -172,6 +196,8 @@ class RunQueueTests(unittest.TestCase):
172 expected.remove('a1:package_qa') 196 expected.remove('a1:package_qa')
173 self.assertEqual(set(tasks), set(expected)) 197 self.assertEqual(set(tasks), set(expected))
174 198
199 self.shutdown(tempdir)
200
175 def test_single_a1_setscenevalid_withdeps(self): 201 def test_single_a1_setscenevalid_withdeps(self):
176 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 202 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
177 cmd = ["bitbake", "b1"] 203 cmd = ["bitbake", "b1"]
@@ -182,6 +208,8 @@ class RunQueueTests(unittest.TestCase):
182 'a1:populate_sysroot'] + ['b1:' + x for x in self.alltasks] 208 'a1:populate_sysroot'] + ['b1:' + x for x in self.alltasks]
183 self.assertEqual(set(tasks), set(expected)) 209 self.assertEqual(set(tasks), set(expected))
184 210
211 self.shutdown(tempdir)
212
185 def test_single_b1_setscenevalid_withdeps(self): 213 def test_single_b1_setscenevalid_withdeps(self):
186 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 214 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
187 cmd = ["bitbake", "b1"] 215 cmd = ["bitbake", "b1"]
@@ -193,6 +221,8 @@ class RunQueueTests(unittest.TestCase):
193 expected.remove('b1:package') 221 expected.remove('b1:package')
194 self.assertEqual(set(tasks), set(expected)) 222 self.assertEqual(set(tasks), set(expected))
195 223
224 self.shutdown(tempdir)
225
196 def test_intermediate_setscenevalid_withdeps(self): 226 def test_intermediate_setscenevalid_withdeps(self):
197 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 227 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
198 cmd = ["bitbake", "b1"] 228 cmd = ["bitbake", "b1"]
@@ -203,6 +233,8 @@ class RunQueueTests(unittest.TestCase):
203 expected.remove('b1:package') 233 expected.remove('b1:package')
204 self.assertEqual(set(tasks), set(expected)) 234 self.assertEqual(set(tasks), set(expected))
205 235
236 self.shutdown(tempdir)
237
206 def test_all_setscenevalid_withdeps(self): 238 def test_all_setscenevalid_withdeps(self):
207 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 239 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
208 cmd = ["bitbake", "b1"] 240 cmd = ["bitbake", "b1"]
@@ -213,6 +245,8 @@ class RunQueueTests(unittest.TestCase):
213 'b1:packagedata_setscene', 'b1:package_qa_setscene', 'b1:populate_sysroot_setscene'] 245 'b1:packagedata_setscene', 'b1:package_qa_setscene', 'b1:populate_sysroot_setscene']
214 self.assertEqual(set(tasks), set(expected)) 246 self.assertEqual(set(tasks), set(expected))
215 247
248 self.shutdown(tempdir)
249
216 def test_multiconfig_setscene_optimise(self): 250 def test_multiconfig_setscene_optimise(self):
217 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 251 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
218 extraenv = { 252 extraenv = {
@@ -232,6 +266,8 @@ class RunQueueTests(unittest.TestCase):
232 expected.remove(x) 266 expected.remove(x)
233 self.assertEqual(set(tasks), set(expected)) 267 self.assertEqual(set(tasks), set(expected))
234 268
269 self.shutdown(tempdir)
270
235 def test_multiconfig_bbmask(self): 271 def test_multiconfig_bbmask(self):
236 # This test validates that multiconfigs can independently mask off 272 # This test validates that multiconfigs can independently mask off
237 # recipes they do not want with BBMASK. It works by having recipes 273 # recipes they do not want with BBMASK. It works by having recipes
@@ -248,11 +284,13 @@ class RunQueueTests(unittest.TestCase):
248 cmd = ["bitbake", "mc:mc-1:fails-mc2", "mc:mc_2:fails-mc1"] 284 cmd = ["bitbake", "mc:mc-1:fails-mc2", "mc:mc_2:fails-mc1"]
249 self.run_bitbakecmd(cmd, tempdir, "", extraenv=extraenv) 285 self.run_bitbakecmd(cmd, tempdir, "", extraenv=extraenv)
250 286
287 self.shutdown(tempdir)
288
251 def test_multiconfig_mcdepends(self): 289 def test_multiconfig_mcdepends(self):
252 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 290 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
253 extraenv = { 291 extraenv = {
254 "BBMULTICONFIG" : "mc-1 mc_2", 292 "BBMULTICONFIG" : "mc-1 mc_2",
255 "BB_SIGNATURE_HANDLER" : "TestMulticonfigDepends", 293 "BB_SIGNATURE_HANDLER" : "basichash",
256 "EXTRA_BBFILES": "${COREBASE}/recipes/fails-mc/*.bb", 294 "EXTRA_BBFILES": "${COREBASE}/recipes/fails-mc/*.bb",
257 } 295 }
258 tasks = self.run_bitbakecmd(["bitbake", "mc:mc-1:f1"], tempdir, "", extraenv=extraenv, cleanup=True) 296 tasks = self.run_bitbakecmd(["bitbake", "mc:mc-1:f1"], tempdir, "", extraenv=extraenv, cleanup=True)
@@ -278,7 +316,15 @@ class RunQueueTests(unittest.TestCase):
278 ["mc_2:a1:%s" % t for t in rerun_tasks] 316 ["mc_2:a1:%s" % t for t in rerun_tasks]
279 self.assertEqual(set(tasks), set(expected)) 317 self.assertEqual(set(tasks), set(expected))
280 318
281 @unittest.skipIf(sys.version_info < (3, 5, 0), 'Python 3.5 or later required') 319 # Check that a multiconfig that doesn't exist rasies a correct error message
320 error_output = self.run_bitbakecmd(["bitbake", "g1"], tempdir, "", extraenv=extraenv, cleanup=True, allowfailure=True)
321 self.assertIn("non-existent task", error_output)
322 # If the word 'Traceback' or 'KeyError' is in the output we've regressed
323 self.assertNotIn("Traceback", error_output)
324 self.assertNotIn("KeyError", error_output)
325
326 self.shutdown(tempdir)
327
282 def test_hashserv_single(self): 328 def test_hashserv_single(self):
283 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 329 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
284 extraenv = { 330 extraenv = {
@@ -304,7 +350,6 @@ class RunQueueTests(unittest.TestCase):
304 350
305 self.shutdown(tempdir) 351 self.shutdown(tempdir)
306 352
307 @unittest.skipIf(sys.version_info < (3, 5, 0), 'Python 3.5 or later required')
308 def test_hashserv_double(self): 353 def test_hashserv_double(self):
309 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 354 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
310 extraenv = { 355 extraenv = {
@@ -329,7 +374,6 @@ class RunQueueTests(unittest.TestCase):
329 374
330 self.shutdown(tempdir) 375 self.shutdown(tempdir)
331 376
332 @unittest.skipIf(sys.version_info < (3, 5, 0), 'Python 3.5 or later required')
333 def test_hashserv_multiple_setscene(self): 377 def test_hashserv_multiple_setscene(self):
334 # Runs e1:do_package_setscene twice 378 # Runs e1:do_package_setscene twice
335 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir: 379 with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
@@ -361,7 +405,6 @@ class RunQueueTests(unittest.TestCase):
361 405
362 def shutdown(self, tempdir): 406 def shutdown(self, tempdir):
363 # Wait for the hashserve socket to disappear else we'll see races with the tempdir cleanup 407 # Wait for the hashserve socket to disappear else we'll see races with the tempdir cleanup
364 while os.path.exists(tempdir + "/hashserve.sock"): 408 while (os.path.exists(tempdir + "/hashserve.sock") or os.path.exists(tempdir + "cache/hashserv.db-wal") or os.path.exists(tempdir + "/bitbake.lock")):
365 time.sleep(0.5) 409 time.sleep(0.5)
366 410
367
diff --git a/bitbake/lib/bb/tests/setup.py b/bitbake/lib/bb/tests/setup.py
new file mode 100644
index 0000000000..e320cdf56f
--- /dev/null
+++ b/bitbake/lib/bb/tests/setup.py
@@ -0,0 +1,358 @@
1#
2# Copyright BitBake Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7from bb.tests.fetch import FetcherTest
8import json
9
10class BitbakeSetupTest(FetcherTest):
11 def setUp(self):
12 super(BitbakeSetupTest, self).setUp()
13
14 self.registrypath = os.path.join(self.tempdir, "bitbake-setup-configurations")
15
16 os.makedirs(self.registrypath)
17 self.git_init(cwd=self.registrypath)
18 self.git('commit --allow-empty -m "Initial commit"', cwd=self.registrypath)
19
20 self.testrepopath = os.path.join(self.tempdir, "test-repo")
21 os.makedirs(self.testrepopath)
22 self.git_init(cwd=self.testrepopath)
23 self.git('commit --allow-empty -m "Initial commit"', cwd=self.testrepopath)
24
25 oeinitbuildenv = """BBPATH=$1
26export BBPATH
27PATH={}:$PATH
28""".format(os.path.join(self.testrepopath, 'scripts'))
29 self.add_file_to_testrepo('oe-init-build-env',oeinitbuildenv, script=True)
30
31 oesetupbuild = """#!/usr/bin/env python3
32import getopt
33import sys
34import os
35import shutil
36opts, args = getopt.getopt(sys.argv[2:], "c:b:", ["no-shell"])
37for option, value in opts:
38 if option == '-c':
39 template = value
40 if option == '-b':
41 builddir = value
42confdir = os.path.join(builddir, 'conf')
43os.makedirs(confdir, exist_ok=True)
44with open(os.path.join(confdir, 'conf-summary.txt'), 'w') as f:
45 f.write(template)
46shutil.copy(os.path.join(os.path.dirname(__file__), 'test-repo/test-file'), confdir)
47with open(os.path.join(builddir, 'init-build-env'), 'w') as f:
48 f.write("BBPATH={}\\nexport BBPATH\\nPATH={}:$PATH".format(builddir, os.path.join(os.path.dirname(__file__), 'test-repo/scripts')))
49"""
50 self.add_file_to_testrepo('scripts/oe-setup-build', oesetupbuild, script=True)
51
52 installbuildtools = """#!/usr/bin/env python3
53import getopt
54import sys
55import os
56
57opts, args = getopt.getopt(sys.argv[1:], "d:", ["downloads-directory="])
58for option, value in opts:
59 if option == '-d':
60 installdir = value
61
62print("Buildtools installed into {}".format(installdir))
63os.makedirs(installdir)
64"""
65 self.add_file_to_testrepo('scripts/install-buildtools', installbuildtools, script=True)
66
67 bitbakeconfigbuild = """#!/usr/bin/env python3
68import os
69import sys
70confdir = os.path.join(os.environ['BBPATH'], 'conf')
71fragment = sys.argv[2]
72with open(os.path.join(confdir, fragment), 'w') as f:
73 f.write('')
74"""
75 self.add_file_to_testrepo('scripts/bitbake-config-build', bitbakeconfigbuild, script=True)
76
77 sometargetexecutable_template = """#!/usr/bin/env python3
78import os
79print("This is {}")
80print("BBPATH is {{}}".format(os.environ["BBPATH"]))
81"""
82 for e_name in ("some-target-executable-1", "some-target-executable-2"):
83 sometargetexecutable = sometargetexecutable_template.format(e_name)
84 self.add_file_to_testrepo('scripts/{}'.format(e_name), sometargetexecutable, script=True)
85
86 def runbbsetup(self, cmd):
87 bbsetup = os.path.abspath(os.path.dirname(__file__) + "/../../../bin/bitbake-setup")
88 return bb.process.run("{} --global-settings {} {}".format(bbsetup, os.path.join(self.tempdir, 'global-config'), cmd))
89
90 def add_json_config_to_registry(self, name, rev, branch):
91 config = """
92{
93 "sources": {
94 "test-repo": {
95 "git-remote": {
96 "remotes": {
97 "origin": {
98 "uri": "file://%s"
99 }
100 },
101 "branch": "%s",
102 "rev": "%s"
103 },
104 "path": "test-repo"
105 }
106 },
107 "description": "Test configuration",
108 "bitbake-setup": {
109 "configurations": [
110 {
111 "name": "gadget",
112 "description": "Gadget build configuration",
113 "oe-template": "test-configuration-gadget",
114 "oe-fragments": ["test-fragment-1"]
115 },
116 {
117 "name": "gizmo",
118 "description": "Gizmo build configuration",
119 "oe-template": "test-configuration-gizmo",
120 "oe-fragments": ["test-fragment-2"]
121 },
122 {
123 "name": "gizmo-env-passthrough",
124 "description": "Gizmo build configuration with environment-passthrough",
125 "bb-layers": ["layerC","layerD/meta-layer"],
126 "oe-fragments": ["test-fragment-1"],
127 "bb-env-passthrough-additions": [
128 "BUILD_ID",
129 "BUILD_DATE",
130 "BUILD_SERVER"
131 ]
132 },
133 {
134 "name": "gizmo-no-fragment",
135 "description": "Gizmo no-fragment template-only build configuration",
136 "oe-template": "test-configuration-gizmo"
137 },
138 {
139 "name": "gadget-notemplate",
140 "description": "Gadget notemplate build configuration",
141 "bb-layers": ["layerA","layerB/meta-layer"],
142 "oe-fragments": ["test-fragment-1"]
143 },
144 {
145 "name": "gizmo-notemplate",
146 "description": "Gizmo notemplate build configuration",
147 "bb-layers": ["layerC","layerD/meta-layer"],
148 "oe-fragments": ["test-fragment-2"]
149 },
150 {
151 "name": "gizmo-notemplate-with-thisdir",
152 "description": "Gizmo notemplate build configuration using THISDIR",
153 "bb-layers": ["layerC","layerD/meta-layer","{THISDIR}/layerE/meta-layer"],
154 "oe-fragments": ["test-fragment-2"]
155 }
156 ]
157 },
158 "version": "1.0"
159}
160""" % (self.testrepopath, branch, rev)
161 os.makedirs(os.path.join(self.registrypath, os.path.dirname(name)), exist_ok=True)
162 with open(os.path.join(self.registrypath, name), 'w') as f:
163 f.write(config)
164 self.git('add {}'.format(name), cwd=self.registrypath)
165 self.git('commit -m "Adding {}"'.format(name), cwd=self.registrypath)
166 return json.loads(config)
167
168 def add_file_to_testrepo(self, name, content, script=False):
169 fullname = os.path.join(self.testrepopath, name)
170 os.makedirs(os.path.join(self.testrepopath, os.path.dirname(name)), exist_ok=True)
171 with open(fullname, 'w') as f:
172 f.write(content)
173 if script:
174 import stat
175 st = os.stat(fullname)
176 os.chmod(fullname, st.st_mode | stat.S_IEXEC)
177 self.git('add {}'.format(name), cwd=self.testrepopath)
178 self.git('commit -m "Adding {}"'.format(name), cwd=self.testrepopath)
179
180 def check_builddir_files(self, buildpath, test_file_content, json_config):
181 with open(os.path.join(buildpath, 'layers', 'test-repo', 'test-file')) as f:
182 self.assertEqual(f.read(), test_file_content)
183 bitbake_config = json_config["bitbake-config"]
184 bb_build_path = os.path.join(buildpath, 'build')
185 bb_conf_path = os.path.join(bb_build_path, 'conf')
186 self.assertTrue(os.path.exists(os.path.join(bb_build_path, 'init-build-env')))
187
188 if "oe-template" in bitbake_config:
189 with open(os.path.join(bb_conf_path, 'conf-summary.txt')) as f:
190 self.assertEqual(f.read(), bitbake_config["oe-template"])
191 with open(os.path.join(bb_conf_path, 'test-file')) as f:
192 self.assertEqual(f.read(), test_file_content)
193 else:
194 with open(os.path.join(bb_conf_path, 'conf-summary.txt')) as f:
195 self.assertIn(bitbake_config["description"], f.read())
196 with open(os.path.join(bb_conf_path, 'bblayers.conf')) as f:
197 bblayers = f.read()
198 for l in bitbake_config["bb-layers"]:
199 if l.startswith('{THISDIR}/'):
200 thisdir_layer = os.path.join(
201 os.path.dirname(json_config["path"]),
202 l.removeprefix("{THISDIR}/"),
203 )
204 self.assertIn(thisdir_layer, bblayers)
205 else:
206 self.assertIn(os.path.join(buildpath, "layers", l), bblayers)
207
208 if 'oe-fragment' in bitbake_config.keys():
209 for f in bitbake_config["oe-fragments"]:
210 self.assertTrue(os.path.exists(os.path.join(bb_conf_path, f)))
211
212 if 'bb-environment-passthrough' in bitbake_config.keys():
213 with open(os.path.join(bb_build_path, 'init-build-env'), 'r') as f:
214 init_build_env = f.read()
215 self.assertTrue('BB_ENV_PASSTHROUGH_ADDITIONS' in init_build_env)
216 self.assertTrue('BUILD_ID' in init_build_env)
217 self.assertTrue('BUILD_DATE' in init_build_env)
218 self.assertTrue('BUILD_SERVER' in init_build_env)
219 # a more throrough test could be to initialize a bitbake build-env, export FOO to the shell environment, set the env-passthrough on it and finally check against 'bitbake-getvar FOO'
220
221
222 def test_setup(self):
223 # unset BBPATH to ensure tests run in isolation from the existing bitbake environment
224 import os
225 if 'BBPATH' in os.environ:
226 del os.environ['BBPATH']
227
228 # check that no arguments works
229 self.runbbsetup("")
230
231 # check that --help works
232 self.runbbsetup("--help")
233
234 # set up global location for top-dir-prefix
235 out = self.runbbsetup("settings set --global default top-dir-prefix {}".format(self.tempdir))
236 settings_path = "{}/global-config".format(self.tempdir)
237 self.assertIn(settings_path, out[0])
238 self.assertIn("From section 'default' the setting 'top-dir-prefix' was changed to", out[0])
239 self.assertIn("Settings written to".format(settings_path), out[0])
240 out = self.runbbsetup("settings set --global default dl-dir {}".format(os.path.join(self.tempdir, 'downloads')))
241 self.assertIn("From section 'default' the setting 'dl-dir' was changed to", out[0])
242 self.assertIn("Settings written to".format(settings_path), out[0])
243
244 # check that writing settings works and then adjust them to point to
245 # test registry repo
246 out = self.runbbsetup("settings set default registry 'git://{};protocol=file;branch=master;rev=master'".format(self.registrypath))
247 settings_path = "{}/bitbake-builds/settings.conf".format(self.tempdir)
248 self.assertIn(settings_path, out[0])
249 self.assertIn("From section 'default' the setting 'registry' was changed to", out[0])
250 self.assertIn("Settings written to".format(settings_path), out[0])
251
252 # check that listing settings works
253 out = self.runbbsetup("settings list")
254 self.assertIn("default top-dir-prefix {}".format(self.tempdir), out[0])
255 self.assertIn("default dl-dir {}".format(os.path.join(self.tempdir, 'downloads')), out[0])
256 self.assertIn("default registry {}".format('git://{};protocol=file;branch=master;rev=master'.format(self.registrypath)), out[0])
257
258 # check that 'list' produces correct output with no configs, one config and two configs
259 out = self.runbbsetup("list")
260 self.assertNotIn("test-config-1", out[0])
261 self.assertNotIn("test-config-2", out[0])
262
263 json_1 = self.add_json_config_to_registry('test-config-1.conf.json', 'master', 'master')
264 out = self.runbbsetup("list")
265 self.assertIn("test-config-1", out[0])
266 self.assertNotIn("test-config-2", out[0])
267
268 json_2 = self.add_json_config_to_registry('config-2/test-config-2.conf.json', 'master', 'master')
269 out = self.runbbsetup("list --write-json={}".format(os.path.join(self.tempdir, "test-configs.json")))
270 self.assertIn("test-config-1", out[0])
271 self.assertIn("test-config-2", out[0])
272 with open(os.path.join(self.tempdir, "test-configs.json")) as f:
273 json_configs = json.load(f)
274 self.assertIn("test-config-1", json_configs)
275 self.assertIn("test-config-2", json_configs)
276
277 # check that init/status/update work
278 # (the latter two should do nothing and say that config hasn't changed)
279 test_file_content = 'initial\n'
280 self.add_file_to_testrepo('test-file', test_file_content)
281
282 # test-config-1 is tested as a registry config, test-config-2 as a local file
283 test_configurations = {'test-config-1': {'cmdline': 'test-config-1',
284 'buildconfigs':('gadget','gizmo',
285 'gizmo-env-passthrough',
286 'gizmo-no-fragment',
287 'gadget-notemplate','gizmo-notemplate')},
288 'test-config-2': {'cmdline': os.path.join(self.registrypath,'config-2/test-config-2.conf.json'),
289 'buildconfigs': ('gadget','gizmo',
290 'gizmo-env-passthrough',
291 'gizmo-no-fragment',
292 'gadget-notemplate','gizmo-notemplate',
293 'gizmo-notemplate-with-thisdir')}
294 }
295 for cf, v in test_configurations.items():
296 for c in v['buildconfigs']:
297 out = self.runbbsetup("init --non-interactive {} {}".format(v['cmdline'], c))
298 buildpath = os.path.join(self.tempdir, 'bitbake-builds', '{}-{}'.format(cf, c))
299 with open(os.path.join(buildpath, 'config', "config-upstream.json")) as f:
300 config_upstream = json.load(f)
301 self.check_builddir_files(buildpath, test_file_content, config_upstream)
302 os.environ['BBPATH'] = os.path.join(buildpath, 'build')
303 out = self.runbbsetup("status")
304 self.assertIn("Configuration in {} has not changed".format(buildpath), out[0])
305 out = self.runbbsetup("update")
306 self.assertIn("Configuration in {} has not changed".format(buildpath), out[0])
307
308 # install buildtools
309 out = self.runbbsetup("install-buildtools")
310 self.assertIn("Buildtools installed into", out[0])
311 self.assertTrue(os.path.exists(os.path.join(buildpath, 'buildtools')))
312
313 # change a file in the test layer repo, make a new commit and
314 # test that status/update correctly report the change and update the config
315 prev_test_file_content = test_file_content
316 test_file_content = 'modified\n'
317 self.add_file_to_testrepo('test-file', test_file_content)
318 for c in ('gadget', 'gizmo',
319 'gizmo-env-passthrough',
320 'gizmo-no-fragment',
321 'gadget-notemplate', 'gizmo-notemplate'):
322 buildpath = os.path.join(self.tempdir, 'bitbake-builds', 'test-config-1-{}'.format(c))
323 os.environ['BBPATH'] = os.path.join(buildpath, 'build')
324 out = self.runbbsetup("status")
325 self.assertIn("Layer repository file://{} checked out into {}/layers/test-repo updated revision master from".format(self.testrepopath, buildpath), out[0])
326 out = self.runbbsetup("update")
327 if c in ('gadget', 'gizmo'):
328 self.assertIn("Existing bitbake configuration directory renamed to {}/build/conf-backup.".format(buildpath), out[0])
329 self.assertIn('-{}+{}'.format(prev_test_file_content, test_file_content), out[0])
330 with open(os.path.join(buildpath, 'config', "config-upstream.json")) as f:
331 config_upstream = json.load(f)
332 self.check_builddir_files(buildpath, test_file_content, config_upstream)
333
334 # make a new branch in the test layer repo, change a file on that branch,
335 # make a new commit, update the top level json config to refer to that branch,
336 # and test that status/update correctly report the change and update the config
337 prev_test_file_content = test_file_content
338 test_file_content = 'modified-in-branch\n'
339 branch = "another-branch"
340 self.git('checkout -b {}'.format(branch), cwd=self.testrepopath)
341 self.add_file_to_testrepo('test-file', test_file_content)
342 json_1 = self.add_json_config_to_registry('test-config-1.conf.json', branch, branch)
343 for c in ('gadget', 'gizmo',
344 'gizmo-env-passthrough',
345 'gizmo-no-fragment',
346 'gadget-notemplate', 'gizmo-notemplate'):
347 buildpath = os.path.join(self.tempdir, 'bitbake-builds', 'test-config-1-{}'.format(c))
348 os.environ['BBPATH'] = os.path.join(buildpath, 'build')
349 out = self.runbbsetup("status")
350 self.assertIn("Configuration in {} has changed:".format(buildpath), out[0])
351 self.assertIn('- "rev": "master"\n+ "rev": "another-branch"', out[0])
352 out = self.runbbsetup("update")
353 if c in ('gadget', 'gizmo'):
354 self.assertIn("Existing bitbake configuration directory renamed to {}/build/conf-backup.".format(buildpath), out[0])
355 self.assertIn('-{}+{}'.format(prev_test_file_content, test_file_content), out[0])
356 with open(os.path.join(buildpath, 'config', "config-upstream.json")) as f:
357 config_upstream = json.load(f)
358 self.check_builddir_files(buildpath, test_file_content, config_upstream)
diff --git a/bitbake/lib/bb/tests/siggen.py b/bitbake/lib/bb/tests/siggen.py
index c21ab4e4fb..0dc67e6cc2 100644
--- a/bitbake/lib/bb/tests/siggen.py
+++ b/bitbake/lib/bb/tests/siggen.py
@@ -17,75 +17,12 @@ import bb.siggen
17 17
18class SiggenTest(unittest.TestCase): 18class SiggenTest(unittest.TestCase):
19 19
20 def test_clean_basepath_simple_target_basepath(self): 20 def test_build_pnid(self):
21 basepath = '/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask' 21 tests = {
22 expected_cleaned = 'helloworld/helloworld_1.2.3.bb:do_sometask' 22 ('', 'helloworld', 'do_sometask') : 'helloworld:do_sometask',
23 ('XX', 'helloworld', 'do_sometask') : 'mc:XX:helloworld:do_sometask',
24 }
23 25
24 actual_cleaned = bb.siggen.clean_basepath(basepath) 26 for t in tests:
27 self.assertEqual(bb.siggen.build_pnid(*t), tests[t])
25 28
26 self.assertEqual(actual_cleaned, expected_cleaned)
27
28 def test_clean_basepath_basic_virtual_basepath(self):
29 basepath = 'virtual:something:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask'
30 expected_cleaned = 'helloworld/helloworld_1.2.3.bb:do_sometask:virtual:something'
31
32 actual_cleaned = bb.siggen.clean_basepath(basepath)
33
34 self.assertEqual(actual_cleaned, expected_cleaned)
35
36 def test_clean_basepath_mc_basepath(self):
37 basepath = 'mc:somemachine:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask'
38 expected_cleaned = 'helloworld/helloworld_1.2.3.bb:do_sometask:mc:somemachine'
39
40 actual_cleaned = bb.siggen.clean_basepath(basepath)
41
42 self.assertEqual(actual_cleaned, expected_cleaned)
43
44 def test_clean_basepath_virtual_long_prefix_basepath(self):
45 basepath = 'virtual:something:A:B:C:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask'
46 expected_cleaned = 'helloworld/helloworld_1.2.3.bb:do_sometask:virtual:something:A:B:C'
47
48 actual_cleaned = bb.siggen.clean_basepath(basepath)
49
50 self.assertEqual(actual_cleaned, expected_cleaned)
51
52 def test_clean_basepath_mc_virtual_basepath(self):
53 basepath = 'mc:somemachine:virtual:something:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask'
54 expected_cleaned = 'helloworld/helloworld_1.2.3.bb:do_sometask:virtual:something:mc:somemachine'
55
56 actual_cleaned = bb.siggen.clean_basepath(basepath)
57
58 self.assertEqual(actual_cleaned, expected_cleaned)
59
60 def test_clean_basepath_mc_virtual_long_prefix_basepath(self):
61 basepath = 'mc:X:virtual:something:C:B:A:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask'
62 expected_cleaned = 'helloworld/helloworld_1.2.3.bb:do_sometask:virtual:something:C:B:A:mc:X'
63
64 actual_cleaned = bb.siggen.clean_basepath(basepath)
65
66 self.assertEqual(actual_cleaned, expected_cleaned)
67
68
69 # def test_clean_basepath_performance(self):
70 # input_basepaths = [
71 # 'mc:X:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask',
72 # 'mc:X:virtual:something:C:B:A:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask',
73 # 'virtual:something:C:B:A:/different/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask',
74 # 'virtual:something:A:/full/path/to/poky/meta/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask',
75 # '/this/is/most/common/input/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask',
76 # '/and/should/be/tested/with/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask',
77 # '/more/weight/recipes-whatever/helloworld/helloworld_1.2.3.bb:do_sometask',
78 # ]
79
80 # time_start = time.time()
81
82 # i = 2000000
83 # while i >= 0:
84 # for basepath in input_basepaths:
85 # bb.siggen.clean_basepath(basepath)
86 # i -= 1
87
88 # elapsed = time.time() - time_start
89 # print('{} ({}s)'.format(self.id(), round(elapsed, 3)))
90
91 # self.assertTrue(False)
diff --git a/bitbake/lib/bb/tests/support/httpserver.py b/bitbake/lib/bb/tests/support/httpserver.py
index 78f7660053..03327e923b 100644
--- a/bitbake/lib/bb/tests/support/httpserver.py
+++ b/bitbake/lib/bb/tests/support/httpserver.py
@@ -3,7 +3,7 @@
3# 3#
4 4
5import http.server 5import http.server
6import multiprocessing 6from bb import multiprocessing
7import os 7import os
8import traceback 8import traceback
9import signal 9import signal
@@ -43,7 +43,7 @@ class HTTPService(object):
43 self.process = multiprocessing.Process(target=self.server.server_start, args=[self.root_dir, self.logger]) 43 self.process = multiprocessing.Process(target=self.server.server_start, args=[self.root_dir, self.logger])
44 44
45 # The signal handler from testimage.bbclass can cause deadlocks here 45 # The signal handler from testimage.bbclass can cause deadlocks here
46 # if the HTTPServer is terminated before it can restore the standard 46 # if the HTTPServer is terminated before it can restore the standard
47 #signal behaviour 47 #signal behaviour
48 orig = signal.getsignal(signal.SIGTERM) 48 orig = signal.getsignal(signal.SIGTERM)
49 signal.signal(signal.SIGTERM, signal.SIG_DFL) 49 signal.signal(signal.SIGTERM, signal.SIG_DFL)
diff --git a/bitbake/lib/bb/tests/utils.py b/bitbake/lib/bb/tests/utils.py
index a7ff33db52..52b7bf85bf 100644
--- a/bitbake/lib/bb/tests/utils.py
+++ b/bitbake/lib/bb/tests/utils.py
@@ -130,6 +130,14 @@ class Checksum(unittest.TestCase):
130 checksum = bb.utils.sha256_file(f.name) 130 checksum = bb.utils.sha256_file(f.name)
131 self.assertEqual(checksum, "fcfbae8bf6b721dbb9d2dc6a9334a58f2031a9a9b302999243f99da4d7f12d0f") 131 self.assertEqual(checksum, "fcfbae8bf6b721dbb9d2dc6a9334a58f2031a9a9b302999243f99da4d7f12d0f")
132 132
133 def test_goh1(self):
134 import hashlib
135 with tempfile.NamedTemporaryFile() as f:
136 f.write(self.filler)
137 f.flush()
138 checksum = bb.utils.goh1_file(f.name)
139 self.assertEqual(checksum, "81191f04d4abf413e5badd234814e4202d9efa73e6f9437e9ddd6b8165b569ef")
140
133class EditMetadataFile(unittest.TestCase): 141class EditMetadataFile(unittest.TestCase):
134 _origfile = """ 142 _origfile = """
135# A comment 143# A comment
@@ -418,7 +426,7 @@ MULTILINE = " stuff \\
418 ['MULTILINE'], 426 ['MULTILINE'],
419 handle_var) 427 handle_var)
420 428
421 testvalue = re.sub('\s+', ' ', value_in_callback.strip()) 429 testvalue = re.sub(r'\s+', ' ', value_in_callback.strip())
422 self.assertEqual(expected_value, testvalue) 430 self.assertEqual(expected_value, testvalue)
423 431
424class EditBbLayersConf(unittest.TestCase): 432class EditBbLayersConf(unittest.TestCase):
@@ -666,3 +674,32 @@ class GetReferencedVars(unittest.TestCase):
666 674
667 layers = [{"SRC_URI"}, {"QT_GIT", "QT_MODULE", "QT_MODULE_BRANCH_PARAM", "QT_GIT_PROTOCOL"}, {"QT_GIT_PROJECT", "QT_MODULE_BRANCH", "BPN"}, {"PN", "SPECIAL_PKGSUFFIX"}] 675 layers = [{"SRC_URI"}, {"QT_GIT", "QT_MODULE", "QT_MODULE_BRANCH_PARAM", "QT_GIT_PROTOCOL"}, {"QT_GIT_PROJECT", "QT_MODULE_BRANCH", "BPN"}, {"PN", "SPECIAL_PKGSUFFIX"}]
668 self.check_referenced("${SRC_URI}", layers) 676 self.check_referenced("${SRC_URI}", layers)
677
678
679class EnvironmentTests(unittest.TestCase):
680 def test_environment(self):
681 os.environ["A"] = "this is A"
682 self.assertIn("A", os.environ)
683 self.assertEqual(os.environ["A"], "this is A")
684 self.assertNotIn("B", os.environ)
685
686 with bb.utils.environment(B="this is B"):
687 self.assertIn("A", os.environ)
688 self.assertEqual(os.environ["A"], "this is A")
689 self.assertIn("B", os.environ)
690 self.assertEqual(os.environ["B"], "this is B")
691
692 self.assertIn("A", os.environ)
693 self.assertEqual(os.environ["A"], "this is A")
694 self.assertNotIn("B", os.environ)
695
696class FilemodeTests(unittest.TestCase):
697 def test_filemode_convert(self):
698 self.assertEqual(0o775, bb.utils.to_filemode("0o775"))
699 self.assertEqual(0o775, bb.utils.to_filemode(0o775))
700 self.assertEqual(0o775, bb.utils.to_filemode("775"))
701 with self.assertRaises(ValueError):
702 bb.utils.to_filemode("xyz")
703 with self.assertRaises(ValueError):
704 bb.utils.to_filemode("999")
705