ADD Support for selectors

Also add some smoke tests in order to guide the correctness of the tests.
48 files changed, 762 insertions(+), 36 deletions(-)

M Makefile
M README.org
A => smoke-tests/bin/curl
A => smoke-tests/success/multiple_selectors/expected_opam/packages/multiple_selectors/multiple_selectors.1.0.0/descr
A => smoke-tests/success/multiple_selectors/expected_opam/packages/multiple_selectors/multiple_selectors.1.0.0/opam
A => smoke-tests/success/multiple_selectors/expected_opam/packages/multiple_selectors/multiple_selectors.1.0.0/url
A => smoke-tests/success/multiple_selectors/hll.conf
A => smoke-tests/success/multiple_selectors/pds.conf
A => smoke-tests/success/multiple_selectors/test
A => smoke-tests/success/pins/expected_opam/packages/pins/pins.1.0.0/descr
A => smoke-tests/success/pins/expected_opam/packages/pins/pins.1.0.0/opam
A => smoke-tests/success/pins/expected_opam/packages/pins/pins.1.0.0/url
A => smoke-tests/success/pins/hll.conf
A => smoke-tests/success/pins/pds.conf
A => smoke-tests/success/pins/pins
A => smoke-tests/success/pins/test
A => smoke-tests/success/selector_map/expected_opam/packages/selector_map/selector_map.1.0.0/descr
A => smoke-tests/success/selector_map/expected_opam/packages/selector_map/selector_map.1.0.0/opam
A => smoke-tests/success/selector_map/expected_opam/packages/selector_map/selector_map.1.0.0/url
A => smoke-tests/success/selector_map/hll.conf
A => smoke-tests/success/selector_map/pds.conf
A => smoke-tests/success/selector_map/test
A => smoke-tests/success/selectors/expected_opam/packages/selectors/selectors.1.0.0/descr
A => smoke-tests/success/selectors/expected_opam/packages/selectors/selectors.1.0.0/opam
A => smoke-tests/success/selectors/expected_opam/packages/selectors/selectors.1.0.0/url
A => smoke-tests/success/selectors/hll.conf
A => smoke-tests/success/selectors/pds.conf
A => smoke-tests/success/selectors/test
A => smoke-tests/success/selectors_test/expected_opam/packages/selectors_test/selectors_test.1.0.0/descr
A => smoke-tests/success/selectors_test/expected_opam/packages/selectors_test/selectors_test.1.0.0/opam
A => smoke-tests/success/selectors_test/expected_opam/packages/selectors_test/selectors_test.1.0.0/url
A => smoke-tests/success/selectors_test/hll.conf
A => smoke-tests/success/selectors_test/pds.conf
A => smoke-tests/success/selectors_test/test
A => smoke-tests/success/simple/expected_opam/packages/simple/simple.1.0.0/descr
A => smoke-tests/success/simple/expected_opam/packages/simple/simple.1.0.0/opam
A => smoke-tests/success/simple/expected_opam/packages/simple/simple.1.0.0/url
A => smoke-tests/success/simple/hll.conf
A => smoke-tests/success/simple/pds.conf
A => smoke-tests/success/simple/test
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/expected_opam/packages/ubiquitious_selector_in_simplest_form/ubiquitious_selector_in_simplest_form.1.0.0/descr
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/expected_opam/packages/ubiquitious_selector_in_simplest_form/ubiquitious_selector_in_simplest_form.1.0.0/opam
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/expected_opam/packages/ubiquitious_selector_in_simplest_form/ubiquitious_selector_in_simplest_form.1.0.0/url
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/hll.conf
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/pds.conf
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/test
A => smoke-tests/test
M src/hll/hll.ml
M Makefile +7 -0
@@ 1,7 1,14 @@ 
+.PHONY: all test smoke-tests
 all:
 	pds
 	$(MAKE) -f pds.mk all
 
+test: smoke-tests
+
 %:
 	pds
 	$(MAKE) -f pds.mk $*
+
+
+smoke-tests: release
+	cd smoke-tests && ./test

          
M README.org +46 -18
@@ 6,24 6,25 @@ only option required is ~--opam-dir~.
 ** Variables
 The following variables can be set in ~hll.conf~.
 
-| Name              | Required | Type        | Description                                                     |
-|-------------------+----------+-------------+-----------------------------------------------------------------|
-| desc              | yes      | string      | Description of the package                                      |
-| maintainer        | yes      | string      | The maintainer of the package                                   |
-| authors           | yes      | string list | List of authors of the repo                                     |
-| homepage          | yes      | string      | Homepage of the repo                                            |
-| bug_reports       | no       | string      | Where bugs should be reported                                   |
-| dev_repo          | no       | string      | URL to where the source of the repo lives                       |
-| url_template      | yes      | string      | URL template to fetch the package                               |
-| url_pattern       | yes      | string      | Pattern to replace in the url template with the tag             |
-| url_protocol      | yes      | string      | Valid options are "git" and "http"                              |
-| available         | no       | string      | Adds an available section to the opam, surrounding [] are added |
-| build_deps        | no       | string list | List of deps needed to build the repo                           |
-| opam_extra_lines  | no       | string list | Add extra lines to the end of the opam                          |
-| deps_blacklist    | no       | string list | List of deps that should not be in the opam package             |
-| deps_map          | no       | table       | Table of package names and the pds deps that compose them       |
-| pds.major_version | yes/no   | int         | The major version of pds the build depends on.                  |
-| pds.version       | yes/no   | string      | The opam string filter describing the version                   |
+| Name                    | Required | Type        | Description                                                     |
+|-------------------------+----------+-------------+-----------------------------------------------------------------|
+| desc                    | yes      | string      | Description of the package                                      |
+| maintainer              | yes      | string      | The maintainer of the package                                   |
+| authors                 | yes      | string list | List of authors of the repo                                     |
+| homepage                | yes      | string      | Homepage of the repo                                            |
+| bug_reports             | no       | string      | Where bugs should be reported                                   |
+| dev_repo                | no       | string      | URL to where the source of the repo lives                       |
+| url_template            | yes      | string      | URL template to fetch the package                               |
+| url_pattern             | yes      | string      | Pattern to replace in the url template with the tag             |
+| url_protocol            | yes      | string      | Valid options are "git" and "http"                              |
+| available               | no       | string      | Adds an available section to the opam, surrounding [] are added |
+| build_deps              | no       | string list | List of deps needed to build the repo                           |
+| opam_extra_lines        | no       | string list | Add extra lines to the end of the opam                          |
+| deps_blacklist          | no       | string list | List of deps that should not be in the opam package             |
+| deps_map                | no       | table       | Table of package names and the pds deps that compose them       |
+| pds.major_version       | yes/no   | int         | The major version of pds the build depends on.                  |
+| pds.version             | yes/no   | string      | The opam string filter describing the version                   |
+| selector_map.<selector> | no       | string      | Map a selector in pds to a pin                                  |
 
 Either the ~pds.major_version~ or ~pds.version~ are required but they are
 mutually exclusive.  This variable selects the ~pds~ version to use to build the

          
@@ 59,6 60,25 @@ url_protocol = "http"
 foo = ["foo_baz", "foo_bar", "foo_zoom"]
 "ocaml-bar" = ["bar_baz", "bar_zoom"]
 #+END_EXAMPLE
+** Selectors
+hll supports selectors, which are a feature from pds for conditional
+compilation.  hll translates selectors into filters on dependencies in opam.
+Selectors in pds can be entirely arbitrary and hll needs a way to map those
+arbitrary selectors to something that opam understands.  By default, hll assumes
+that the selectors directly map to opam's OS variable.  For example, the
+selector ~linux~ maps to the ~linux~ OS type, and ~freebsd~ to ~freebsd~.  This
+mapping can be redefined in the ~selector_map~ section of ~hll.conf~.  For
+example, if the pds configuration has a selector called ~bar~ that should map to
+the ~freebsd~ filter in opam, the ~selector_map~ would look like:
+
+#+BEGIN_SRC
+[selector_map]
+bar = "freebsd"
+#+END_SRC
+
+There is one case in which the ~selector_map~ is ignored, and that is when pins
+are specified for a dependency.  hll assumes that the pin, which is explicitly
+set, is always right.
 * Changelog
 ** 1.0.0
 - Turn a ~pds.conf~ and a ~hll.conf~ into an OPAM package.

          
@@ 86,3 106,11 @@ foo = ["foo_baz", "foo_bar", "foo_zoom"]
   tests.
 - Dependencies that exist only in the ~tests~ table will receive a ~test~
   constraint in the ~depends~ list.
+** 2.9
+- Support making test deps regular deps.  This can be used to install those
+  dependencies in a testing environment.  This is needed because telling opam to
+  run tests itself runs tests for the thing being installed and all of its
+  dependencies, which is almost never what one wants.
+** 2.10
+- Support selectors.
+

          
A => smoke-tests/bin/curl +3 -0
@@ 0,0 1,3 @@ 
+#! /usr/bin/env bash
+
+echo foo

          
A => smoke-tests/success/multiple_selectors/expected_opam/packages/multiple_selectors/multiple_selectors.1.0.0/descr +2 -0
@@ 0,0 1,2 @@ 
+Testing
+

          
A => smoke-tests/success/multiple_selectors/expected_opam/packages/multiple_selectors/multiple_selectors.1.0.0/opam +34 -0
@@ 0,0 1,34 @@ 
+opam-version: "1.2"
+maintainer: "test@test.com"
+build: [
+	[make "-j%{jobs}%"]
+]
+
+build-test: [
+	[make "-j%{jobs}%" "test"]
+]
+
+install: [
+	[make "PREFIX=%{prefix}%" "install"]
+]
+
+remove: [
+	[make "PREFIX=%{prefix}%" "remove"]
+]
+
+depends: [
+	"bye" { freebsd | linux }
+	"goodbye" { linux }
+	"hi" { !(freebsd | linux) }
+	"ocamlfind"
+	"pds" { build & (>= "5" & < "6") }
+]
+
+authors: [
+	"test@test.com"
+]
+
+homepage: "https://bitbucket.org/mimirops/hll"
+bug-reports: "https://bitbucket.org/mimirops/hll/issues"
+dev-repo: "git@bitbucket.org:mimirops/hll.git"
+

          
A => smoke-tests/success/multiple_selectors/expected_opam/packages/multiple_selectors/multiple_selectors.1.0.0/url +3 -0
@@ 0,0 1,3 @@ 
+archive: "https://bitbucket.org/mimirops/hll/get/1.0.0.tar.gz"
+checksum: "d3b07384d113edec49eaa6238ad5ff00"
+

          
A => smoke-tests/success/multiple_selectors/hll.conf +13 -0
@@ 0,0 1,13 @@ 
+pds = { major_version = 5 }
+
+desc = "Testing"
+maintainer = "test@test.com"
+authors = [ "test@test.com" ]
+homepage = "https://bitbucket.org/mimirops/hll"
+bug_reports = "https://bitbucket.org/mimirops/hll/issues"
+dev_repo = "git@bitbucket.org:mimirops/hll.git"
+
+url_template = "https://bitbucket.org/mimirops/hll/get/{tag}.tar.gz"
+url_pattern = "{tag}"
+url_protocol = "http"
+

          
A => smoke-tests/success/multiple_selectors/pds.conf +9 -0
@@ 0,0 1,9 @@ 
+[src.foo]
+install = false
+deps = [ "hi" ]
+
+[src.foo.selector.freebsd]
+deps = [ "bye" ]
+
+[src.foo.selector.linux]
+deps = [ "bye", "goodbye" ]

          
A => smoke-tests/success/multiple_selectors/test +4 -0
@@ 0,0 1,4 @@ 
+#! /usr/bin/env bash
+
+mkdir -p opam
+../../../build/release/hll/hll.native generate --opam-dir "$1"  --tag 1.0.0

          
A => smoke-tests/success/pins/expected_opam/packages/pins/pins.1.0.0/descr +2 -0
@@ 0,0 1,2 @@ 
+Testing
+

          
A => smoke-tests/success/pins/expected_opam/packages/pins/pins.1.0.0/opam +32 -0
@@ 0,0 1,32 @@ 
+opam-version: "1.2"
+maintainer: "test@test.com"
+build: [
+	[make "-j%{jobs}%"]
+]
+
+build-test: [
+	[make "-j%{jobs}%" "test"]
+]
+
+install: [
+	[make "PREFIX=%{prefix}%" "install"]
+]
+
+remove: [
+	[make "PREFIX=%{prefix}%" "remove"]
+]
+
+depends: [
+	"hi" { >= "3" }
+	"ocamlfind"
+	"pds" { build & (>= "5" & < "6") }
+]
+
+authors: [
+	"test@test.com"
+]
+
+homepage: "https://bitbucket.org/mimirops/hll"
+bug-reports: "https://bitbucket.org/mimirops/hll/issues"
+dev-repo: "git@bitbucket.org:mimirops/hll.git"
+

          
A => smoke-tests/success/pins/expected_opam/packages/pins/pins.1.0.0/url +3 -0
@@ 0,0 1,3 @@ 
+archive: "https://bitbucket.org/mimirops/hll/get/1.0.0.tar.gz"
+checksum: "d3b07384d113edec49eaa6238ad5ff00"
+

          
A => smoke-tests/success/pins/hll.conf +13 -0
@@ 0,0 1,13 @@ 
+pds = { major_version = 5 }
+
+desc = "Testing"
+maintainer = "test@test.com"
+authors = [ "test@test.com" ]
+homepage = "https://bitbucket.org/mimirops/hll"
+bug_reports = "https://bitbucket.org/mimirops/hll/issues"
+dev_repo = "git@bitbucket.org:mimirops/hll.git"
+
+url_template = "https://bitbucket.org/mimirops/hll/get/{tag}.tar.gz"
+url_pattern = "{tag}"
+url_protocol = "http"
+

          
A => smoke-tests/success/pins/pds.conf +3 -0
@@ 0,0 1,3 @@ 
+[src.foo]
+install = false
+deps = [ "hi" ]

          
A => smoke-tests/success/pins/pins +1 -0
@@ 0,0 1,1 @@ 
+hi >= "3"
  No newline at end of file

          
A => smoke-tests/success/pins/test +4 -0
@@ 0,0 1,4 @@ 
+#! /usr/bin/env bash
+
+mkdir -p opam
+../../../build/release/hll/hll.native generate --opam-dir "$1"  --tag 1.0.0 --pins ./pins

          
A => smoke-tests/success/selector_map/expected_opam/packages/selector_map/selector_map.1.0.0/descr +2 -0
@@ 0,0 1,2 @@ 
+Testing
+

          
A => smoke-tests/success/selector_map/expected_opam/packages/selector_map/selector_map.1.0.0/opam +33 -0
@@ 0,0 1,33 @@ 
+opam-version: "1.2"
+maintainer: "test@test.com"
+build: [
+	[make "-j%{jobs}%"]
+]
+
+build-test: [
+	[make "-j%{jobs}%" "test"]
+]
+
+install: [
+	[make "PREFIX=%{prefix}%" "install"]
+]
+
+remove: [
+	[make "PREFIX=%{prefix}%" "remove"]
+]
+
+depends: [
+	"bye" { freebsd }
+	"hi" { !(freebsd) }
+	"ocamlfind"
+	"pds" { build & (>= "5" & < "6") }
+]
+
+authors: [
+	"test@test.com"
+]
+
+homepage: "https://bitbucket.org/mimirops/hll"
+bug-reports: "https://bitbucket.org/mimirops/hll/issues"
+dev-repo: "git@bitbucket.org:mimirops/hll.git"
+

          
A => smoke-tests/success/selector_map/expected_opam/packages/selector_map/selector_map.1.0.0/url +3 -0
@@ 0,0 1,3 @@ 
+archive: "https://bitbucket.org/mimirops/hll/get/1.0.0.tar.gz"
+checksum: "d3b07384d113edec49eaa6238ad5ff00"
+

          
A => smoke-tests/success/selector_map/hll.conf +15 -0
@@ 0,0 1,15 @@ 
+pds = { major_version = 5 }
+
+desc = "Testing"
+maintainer = "test@test.com"
+authors = [ "test@test.com" ]
+homepage = "https://bitbucket.org/mimirops/hll"
+bug_reports = "https://bitbucket.org/mimirops/hll/issues"
+dev_repo = "git@bitbucket.org:mimirops/hll.git"
+
+url_template = "https://bitbucket.org/mimirops/hll/get/{tag}.tar.gz"
+url_pattern = "{tag}"
+url_protocol = "http"
+
+[selector_map]
+bar = "freebsd"

          
A => smoke-tests/success/selector_map/pds.conf +6 -0
@@ 0,0 1,6 @@ 
+[src.foo]
+install = false
+deps = [ "hi" ]
+
+[src.foo.selector.bar]
+deps = [ "bye" ]

          
A => smoke-tests/success/selector_map/test +4 -0
@@ 0,0 1,4 @@ 
+#! /usr/bin/env bash
+
+mkdir -p opam
+../../../build/release/hll/hll.native generate --opam-dir "$1"  --tag 1.0.0

          
A => smoke-tests/success/selectors/expected_opam/packages/selectors/selectors.1.0.0/descr +2 -0
@@ 0,0 1,2 @@ 
+Testing
+

          
A => smoke-tests/success/selectors/expected_opam/packages/selectors/selectors.1.0.0/opam +33 -0
@@ 0,0 1,33 @@ 
+opam-version: "1.2"
+maintainer: "test@test.com"
+build: [
+	[make "-j%{jobs}%"]
+]
+
+build-test: [
+	[make "-j%{jobs}%" "test"]
+]
+
+install: [
+	[make "PREFIX=%{prefix}%" "install"]
+]
+
+remove: [
+	[make "PREFIX=%{prefix}%" "remove"]
+]
+
+depends: [
+	"bye" { freebsd }
+	"hi" { !(freebsd) }
+	"ocamlfind"
+	"pds" { build & (>= "5" & < "6") }
+]
+
+authors: [
+	"test@test.com"
+]
+
+homepage: "https://bitbucket.org/mimirops/hll"
+bug-reports: "https://bitbucket.org/mimirops/hll/issues"
+dev-repo: "git@bitbucket.org:mimirops/hll.git"
+

          
A => smoke-tests/success/selectors/expected_opam/packages/selectors/selectors.1.0.0/url +3 -0
@@ 0,0 1,3 @@ 
+archive: "https://bitbucket.org/mimirops/hll/get/1.0.0.tar.gz"
+checksum: "d3b07384d113edec49eaa6238ad5ff00"
+

          
A => smoke-tests/success/selectors/hll.conf +12 -0
@@ 0,0 1,12 @@ 
+pds = { major_version = 5 }
+
+desc = "Testing"
+maintainer = "test@test.com"
+authors = [ "test@test.com" ]
+homepage = "https://bitbucket.org/mimirops/hll"
+bug_reports = "https://bitbucket.org/mimirops/hll/issues"
+dev_repo = "git@bitbucket.org:mimirops/hll.git"
+
+url_template = "https://bitbucket.org/mimirops/hll/get/{tag}.tar.gz"
+url_pattern = "{tag}"
+url_protocol = "http"

          
A => smoke-tests/success/selectors/pds.conf +6 -0
@@ 0,0 1,6 @@ 
+[src.foo]
+install = false
+deps = [ "hi" ]
+
+[src.foo.selector.freebsd]
+deps = [ "bye" ]

          
A => smoke-tests/success/selectors/test +4 -0
@@ 0,0 1,4 @@ 
+#! /usr/bin/env bash
+
+mkdir -p opam
+../../../build/release/hll/hll.native generate --opam-dir "$1"  --tag 1.0.0

          
A => smoke-tests/success/selectors_test/expected_opam/packages/selectors_test/selectors_test.1.0.0/descr +2 -0
@@ 0,0 1,2 @@ 
+Testing
+

          
A => smoke-tests/success/selectors_test/expected_opam/packages/selectors_test/selectors_test.1.0.0/opam +34 -0
@@ 0,0 1,34 @@ 
+opam-version: "1.2"
+maintainer: "test@test.com"
+build: [
+	[make "-j%{jobs}%"]
+]
+
+build-test: [
+	[make "-j%{jobs}%" "test"]
+]
+
+install: [
+	[make "PREFIX=%{prefix}%" "install"]
+]
+
+remove: [
+	[make "PREFIX=%{prefix}%" "remove"]
+]
+
+depends: [
+	"bar" { test & (freebsd) }
+	"bye" { freebsd }
+	"hi" { !(freebsd) }
+	"ocamlfind"
+	"pds" { build & (>= "5" & < "6") }
+]
+
+authors: [
+	"test@test.com"
+]
+
+homepage: "https://bitbucket.org/mimirops/hll"
+bug-reports: "https://bitbucket.org/mimirops/hll/issues"
+dev-repo: "git@bitbucket.org:mimirops/hll.git"
+

          
A => smoke-tests/success/selectors_test/expected_opam/packages/selectors_test/selectors_test.1.0.0/url +3 -0
@@ 0,0 1,3 @@ 
+archive: "https://bitbucket.org/mimirops/hll/get/1.0.0.tar.gz"
+checksum: "d3b07384d113edec49eaa6238ad5ff00"
+

          
A => smoke-tests/success/selectors_test/hll.conf +12 -0
@@ 0,0 1,12 @@ 
+pds = { major_version = 5 }
+
+desc = "Testing"
+maintainer = "test@test.com"
+authors = [ "test@test.com" ]
+homepage = "https://bitbucket.org/mimirops/hll"
+bug_reports = "https://bitbucket.org/mimirops/hll/issues"
+dev_repo = "git@bitbucket.org:mimirops/hll.git"
+
+url_template = "https://bitbucket.org/mimirops/hll/get/{tag}.tar.gz"
+url_pattern = "{tag}"
+url_protocol = "http"

          
A => smoke-tests/success/selectors_test/pds.conf +9 -0
@@ 0,0 1,9 @@ 
+[src.foo]
+install = false
+deps = [ "hi" ]
+
+[src.foo.selector.freebsd]
+deps = [ "bye" ]
+
+[tests.foo.selector.freebsd]
+deps = [ "foo", "bye", "bar" ]

          
A => smoke-tests/success/selectors_test/test +4 -0
@@ 0,0 1,4 @@ 
+#! /usr/bin/env bash
+
+mkdir -p opam
+../../../build/release/hll/hll.native generate --opam-dir "$1"  --tag 1.0.0

          
A => smoke-tests/success/simple/expected_opam/packages/simple/simple.1.0.0/descr +2 -0
@@ 0,0 1,2 @@ 
+Testing
+

          
A => smoke-tests/success/simple/expected_opam/packages/simple/simple.1.0.0/opam +32 -0
@@ 0,0 1,32 @@ 
+opam-version: "1.2"
+maintainer: "test@test.com"
+build: [
+	[make "-j%{jobs}%"]
+]
+
+build-test: [
+	[make "-j%{jobs}%" "test"]
+]
+
+install: [
+	[make "PREFIX=%{prefix}%" "install"]
+]
+
+remove: [
+	[make "PREFIX=%{prefix}%" "remove"]
+]
+
+depends: [
+	"hi"
+	"ocamlfind"
+	"pds" { build & (>= "5" & < "6") }
+]
+
+authors: [
+	"test@test.com"
+]
+
+homepage: "https://bitbucket.org/mimirops/hll"
+bug-reports: "https://bitbucket.org/mimirops/hll/issues"
+dev-repo: "git@bitbucket.org:mimirops/hll.git"
+

          
A => smoke-tests/success/simple/expected_opam/packages/simple/simple.1.0.0/url +3 -0
@@ 0,0 1,3 @@ 
+archive: "https://bitbucket.org/mimirops/hll/get/1.0.0.tar.gz"
+checksum: "d3b07384d113edec49eaa6238ad5ff00"
+

          
A => smoke-tests/success/simple/hll.conf +13 -0
@@ 0,0 1,13 @@ 
+pds = { major_version = 5 }
+
+desc = "Testing"
+maintainer = "test@test.com"
+authors = [ "test@test.com" ]
+homepage = "https://bitbucket.org/mimirops/hll"
+bug_reports = "https://bitbucket.org/mimirops/hll/issues"
+dev_repo = "git@bitbucket.org:mimirops/hll.git"
+
+url_template = "https://bitbucket.org/mimirops/hll/get/{tag}.tar.gz"
+url_pattern = "{tag}"
+url_protocol = "http"
+

          
A => smoke-tests/success/simple/pds.conf +3 -0
@@ 0,0 1,3 @@ 
+[src.foo]
+install = false
+deps = [ "hi" ]

          
A => smoke-tests/success/simple/test +4 -0
@@ 0,0 1,4 @@ 
+#! /usr/bin/env bash
+
+mkdir -p opam
+../../../build/release/hll/hll.native generate --opam-dir "$1"  --tag 1.0.0

          
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/expected_opam/packages/ubiquitious_selector_in_simplest_form/ubiquitious_selector_in_simplest_form.1.0.0/descr +2 -0
@@ 0,0 1,2 @@ 
+Testing
+

          
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/expected_opam/packages/ubiquitious_selector_in_simplest_form/ubiquitious_selector_in_simplest_form.1.0.0/opam +33 -0
@@ 0,0 1,33 @@ 
+opam-version: "1.2"
+maintainer: "test@test.com"
+build: [
+	[make "-j%{jobs}%"]
+]
+
+build-test: [
+	[make "-j%{jobs}%" "test"]
+]
+
+install: [
+	[make "PREFIX=%{prefix}%" "install"]
+]
+
+remove: [
+	[make "PREFIX=%{prefix}%" "remove"]
+]
+
+depends: [
+	"bye" { freebsd }
+	"hi"
+	"ocamlfind"
+	"pds" { build & (>= "5" & < "6") }
+]
+
+authors: [
+	"test@test.com"
+]
+
+homepage: "https://bitbucket.org/mimirops/hll"
+bug-reports: "https://bitbucket.org/mimirops/hll/issues"
+dev-repo: "git@bitbucket.org:mimirops/hll.git"
+

          
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/expected_opam/packages/ubiquitious_selector_in_simplest_form/ubiquitious_selector_in_simplest_form.1.0.0/url +3 -0
@@ 0,0 1,3 @@ 
+archive: "https://bitbucket.org/mimirops/hll/get/1.0.0.tar.gz"
+checksum: "d3b07384d113edec49eaa6238ad5ff00"
+

          
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/hll.conf +12 -0
@@ 0,0 1,12 @@ 
+pds = { major_version = 5 }
+
+desc = "Testing"
+maintainer = "test@test.com"
+authors = [ "test@test.com" ]
+homepage = "https://bitbucket.org/mimirops/hll"
+bug_reports = "https://bitbucket.org/mimirops/hll/issues"
+dev_repo = "git@bitbucket.org:mimirops/hll.git"
+
+url_template = "https://bitbucket.org/mimirops/hll/get/{tag}.tar.gz"
+url_pattern = "{tag}"
+url_protocol = "http"

          
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/pds.conf +6 -0
@@ 0,0 1,6 @@ 
+[src.foo]
+install = false
+deps = [ "hi" ]
+
+[src.foo.selector.freebsd]
+deps = [ "hi", "bye" ]

          
A => smoke-tests/success/ubiquitious_selector_in_simplest_form/test +4 -0
@@ 0,0 1,4 @@ 
+#! /usr/bin/env bash
+
+mkdir -p opam
+../../../build/release/hll/hll.native generate --opam-dir "$1"  --tag 1.0.0

          
A => smoke-tests/test +39 -0
@@ 0,0 1,39 @@ 
+#! /usr/bin/env bash
+
+set -e
+set -u
+set -o pipefail
+
+tmp_dir="$(mktemp -d)"
+
+trap "rm -rf $tmp_dir" EXIT
+
+export PATH=$PWD/bin:$PATH
+
+num_tests="$(ls success | wc -l | awk '{print $1}')"
+
+echo "1..${num_tests}" > ../build/release/hll/smoke_tests.tap
+
+test_count=1
+failed_test=0
+
+for test in success/*; do
+    pushd "$test" > /dev/null
+    test_name="$(basename "$test")"
+    echo "Testing $test_name"
+    mkdir -p "$tmp_dir/$test_name"
+    set +e
+    ./test "$tmp_dir/$test_name" && diff -r "$tmp_dir/$test_name" expected_opam
+    if [[ "$?" -eq 0 ]]; then
+	echo "ok ${test_count} ${test_name}" >> ../../../build/release/hll/smoke_tests.tap
+    else
+	echo "not ok ${test_count} ${test_name}" >> ../../../build/release/hll/smoke_tests.tap
+	failed_test=1
+    fi
+    set -e
+    echo "Passed $(basename $test_name)"
+    popd > /dev/null
+    test_count=$((test_count + 1))
+done
+
+exit "$failed_test"

          
M src/hll/hll.ml +240 -18
@@ 49,6 49,20 @@ module Cmdline = struct
             info "help" ~doc)
 end
 
+let default_selector_map =
+  let make_pair x = (x, x) in
+  String_map.of_list
+    [ make_pair "darwin"
+    ; make_pair "linux"
+    ; make_pair "freebsd"
+    ; make_pair "openbsd"
+    ; make_pair "netbsd"
+    ; make_pair "dragonfly"
+    ; make_pair "cygwin"
+    ; make_pair "win32"
+    ; make_pair "unix"
+    ]
+
 let value_opt ~default = function
   | Some v -> v
   | None -> default

          
@@ 105,7 119,7 @@ module Generate = struct
     match t.url_protocol with
       | Git ->
         Printf.sprintf "git: \"%s\"\n" url
-      | Http -> begin
+      | Http ->
         let lines = P.read_stdout "curl" [| "-L"; url |] in
         (*
          * Process is, unfortunately, somewhat lame in that it breaks lines up

          
@@ 115,7 129,6 @@ module Generate = struct
         let data = String.concat "\n" lines in
         let hex = Digest.string data |> Digest.to_hex in
         Printf.sprintf "archive: \"%s\"\nchecksum: \"%s\"\n" url hex
-      end
 
   let format_external_deps deps pins =
     List.map

          
@@ 263,6 276,7 @@ module Generate_io = struct
              ; available : string
              ; opam_extra_lines : string list
              ; pds_version : Generate.Pds_version.t
+             ; selector_map : string String_map.t
              }
 
     let url_protocol_of_string = function

          
@@ 270,6 284,27 @@ module Generate_io = struct
       | "http" -> Generate.Http
       | s -> failwith (Printf.sprintf "Invalid url_protocol (%s) in hll configuration" s)
 
+    let selector_map table =
+      TomlTypes.Table.(
+        fold
+          (fun key v selectors ->
+             let filter =
+               value_exn
+                 ~msg:"Selector map must be string to string"
+                 TomlLenses.(get v string)
+             in
+             String_map.add (Key.to_string key) filter selectors)
+          table
+          String_map.empty)
+
+    let get_selector_map hll_conf =
+      let sm = TomlLenses.(get hll_conf (key "selector_map" |-- table)) in
+      match sm with
+        | Some table ->
+          selector_map table
+        | None ->
+          default_selector_map
+
     let read fname =
       let hll_conf = Toml.Parser.(from_filename fname |> unsafe) in
       let maintainer =

          
@@ 359,6 394,7 @@ module Generate_io = struct
           | (_, Some v) ->
             Generate.Pds_version.Version v
       in
+      let selector_map = get_selector_map hll_conf in
       { maintainer = maintainer
       ; url_template = url_template
       ; url_protocol = url_protocol_of_string url_protocol

          
@@ 374,15 410,20 @@ module Generate_io = struct
       ; build_deps = String_set.of_list build_deps
       ; opam_extra_lines = opam_extra_lines
       ; pds_version = pds_version
+      ; selector_map = selector_map
       }
   end
 
   module Pds_conf = struct
-    type t = { deps : String_set.t
-             ; test_deps : String_set.t
+    type t = { deps : string list String_map.t (* Dep -> Selectors *)
+             ; tests_deps : string list String_map.t (* Dep -> Selectors *)
              ; targets : String_set.t
+             ; selectors : String_set.t
+             ; used_selectors : String_set.t
              }
 
+    let default_selector = ""
+
     let list_keys table =
       TomlTypes.Table.(
         fold

          
@@ 394,13 435,80 @@ module Generate_io = struct
       TomlTypes.Table.(
         fold
           (fun key v deps ->
-            deps @ value_opt
-              ~default:[]
-              TomlLenses.(get v (table |-- key "deps" |-- array |-- strings)))
+             deps @ value_opt
+               ~default:[]
+               TomlLenses.(get v (table |-- key "deps" |-- array |-- strings)))
           table
           [])
 
-    let read test_deps_as_regular_deps fname =
+    (* Merge entries in a table and append lists. *)
+    let merge k v1 v2 =
+      match (v1, v2) with
+        | (Some v1, Some v2) -> Some (v1 @ v2)
+        | (Some v, None)
+        | (None, Some v) -> Some v
+        | (None, None) -> assert false
+
+    (* Return a tuple of to String_map's.  The first is the map for builds and
+       the second is the deps for tests. *)
+    let selector_deps pds_conf =
+      (* Fold but specifically pick out the selectors of the entries in the
+         table. *)
+      let fold_selectors tbl f init =
+        TomlTypes.Table.fold
+          (fun build v acc ->
+             let selector =
+               value_opt
+                 ~default:TomlTypes.Table.empty
+                 TomlLenses.(get v (table |-- key "selector" |-- table))
+             in
+             f selector acc)
+          tbl
+          init
+      in
+      (* Look over all of the selectors in a table and merge their deps ->
+         selector map with the passed in map. *)
+      let merge_deps selector acc =
+        TomlTypes.Table.fold
+          (fun slct v acc ->
+             let slct = TomlTypes.Table.Key.to_string slct in
+             let deps =
+               value_opt
+                 ~default:[]
+                 TomlLenses.(get v (table |-- key "deps" |-- array |-- strings))
+             in
+             deps
+             |> ListLabels.map ~f:(fun d -> (d, [slct]))
+             |> String_map.of_list
+             |> String_map.merge merge acc)
+          selector
+          acc
+      in
+      let src =
+        value_opt
+          ~default:TomlTypes.Table.empty
+          TomlLenses.(get pds_conf (key "src" |-- table))
+      in
+      let tests =
+        value_opt
+          ~default:TomlTypes.Table.empty
+          TomlLenses.(get pds_conf (key "tests" |-- table))
+      in
+      let src_deps =
+        fold_selectors
+          src
+          merge_deps
+          String_map.empty
+      in
+      let tests_deps =
+        fold_selectors
+          tests
+          merge_deps
+          String_map.empty
+      in
+      (src_deps, tests_deps)
+
+    let read test_deps_as_regular_deps fname sm =
       let pds_conf = Toml.Parser.(from_filename fname |> unsafe) in
       let srcs =
         value_opt

          
@@ 412,25 520,71 @@ module Generate_io = struct
           ~default:TomlTypes.Table.empty
           TomlLenses.(get pds_conf (key "tests" |-- table))
       in
+      let src_default_deps =
+        String_map.of_list
+          (ListLabels.map ~f:(fun d -> (d, [default_selector])) (list_deps srcs))
+      in
+      let tests_default_deps =
+        String_map.of_list
+          (ListLabels.map ~f:(fun d -> (d, [default_selector])) (list_deps tests))
+      in
       let targets = String_set.of_list (list_keys srcs) in
-      let deps = String_set.of_list (list_deps srcs) in
-      let test_deps = String_set.of_list (list_deps tests) in
+      let (deps, tests_deps) = selector_deps pds_conf in
+      (* Convert the selectors via the map *)
+      (* TODO Make this get less exceptiony *)
+      let deps =
+        String_map.merge
+          merge
+          (String_map.map (ListLabels.map ~f:(CCFun.flip String_map.find sm)) deps)
+          src_default_deps
+      in
+      let tests_deps =
+        String_map.merge
+          merge
+          (String_map.map (ListLabels.map ~f:(CCFun.flip String_map.find sm)) tests_deps)
+          tests_default_deps
+      in
       (* Those deps that are just in the test section *)
-      let test_only_deps = String_set.diff test_deps deps in
+      let tests_only_deps = String_map.filter (fun k _ -> not (String_map.mem k deps)) tests_deps in
+      let selectors = String_set.of_list (ListLabels.map ~f:snd (String_map.bindings sm)) in
+      let used_selectors =
+        String_set.remove
+          default_selector
+          (String_set.of_list
+             (List.concat
+                (ListLabels.map ~f:snd (String_map.bindings deps) @
+                 ListLabels.map ~f:snd (String_map.bindings tests_only_deps))))
+      in
       if not test_deps_as_regular_deps then
         { deps = deps
-        ; test_deps = test_only_deps
+        ; tests_deps = tests_only_deps
         ; targets = targets
+        ; selectors = selectors
+        ; used_selectors = used_selectors
         }
       else
-        { deps = String_set.union deps test_only_deps
-        ; test_deps = String_set.empty
+        { deps = String_map.merge merge deps tests_only_deps
+        ; tests_deps = String_map.empty
         ; targets = targets
+        ; selectors = selectors
+        ; used_selectors = used_selectors
         }
 
-    let external_deps t = String_set.diff t.deps t.targets
+    let external_deps t =
+      String_set.diff
+        (String_set.of_list (ListLabels.map ~f:fst (String_map.bindings t.deps)))
+        t.targets
 
-    let test_external_deps t = String_set.diff t.test_deps t.targets
+    let test_external_deps t =
+      String_set.diff
+        (String_set.of_list (ListLabels.map ~f:fst (String_map.bindings t.tests_deps)))
+        t.targets
+
+    let only_in_default_selector v _ = v = [default_selector]
+
+    let has_default_selector v _ = ListLabels.mem default_selector ~set:v
+
+    let remove_default_selector v _ = ListLabels.filter ~f:((<>) default_selector) v
   end
 
   module Pins = struct

          
@@ 495,6 649,73 @@ let maybe_load_pins = function
   | Some file -> Generate_io.Pins.read file
   | None -> Generate_io.Pins.empty
 
+(* TODO Clean up all the duplication *)
+let merge_pins_with_selector_deps pins pds_conf =
+  let pins =
+    String_map.fold
+      (fun dep v acc ->
+         (* If there is already a pin, it overrides anything we want to do. *)
+         match String_map.get dep acc with
+           | Some _ ->
+             acc
+           | None when Generate_io.Pds_conf.only_in_default_selector v pds_conf ->
+             let used_selectors =
+               String_set.elements pds_conf.Generate_io.Pds_conf.used_selectors
+             in
+             if used_selectors <> [] then
+               let pin = "!(" ^ (String.concat " | " used_selectors) ^ ")" in
+               String_map.add dep pin acc
+             else
+               acc
+           | None when Generate_io.Pds_conf.has_default_selector v pds_conf ->
+             let used_selectors = pds_conf.Generate_io.Pds_conf.used_selectors in
+             let v = String_set.of_list (Generate_io.Pds_conf.remove_default_selector v pds_conf) in
+             let negative_selectors = String_set.diff used_selectors v in
+             if not (String_set.is_empty negative_selectors) then
+               let pin =
+                 "!(" ^ (String.concat " | " (String_set.elements negative_selectors)) ^ ")"
+               in
+               String_map.add dep pin acc
+             else
+               acc
+           | None ->
+             let pin = String.concat " | " v in
+             String_map.add dep pin acc)
+      pds_conf.Generate_io.Pds_conf.deps
+      pins
+  in
+  String_map.fold
+    (fun dep v acc ->
+       (* If there is already a pin, it overrides anything we want to do. *)
+       match String_map.get dep acc with
+         | Some _ ->
+           acc
+         | None when Generate_io.Pds_conf.only_in_default_selector v pds_conf ->
+           let used_selectors =
+             String_set.elements pds_conf.Generate_io.Pds_conf.used_selectors
+           in
+           if used_selectors <> [] then
+               let pin = "test & !(" ^ (String.concat " | " used_selectors) ^ ")" in
+               String_map.add dep pin acc
+           else
+             acc
+         | None when Generate_io.Pds_conf.has_default_selector v pds_conf ->
+           let selectors = pds_conf.Generate_io.Pds_conf.selectors in
+           let v = String_set.of_list (Generate_io.Pds_conf.remove_default_selector v pds_conf) in
+           let negative_selectors = String_set.diff selectors v in
+           let pin =
+             if not (String_set.is_empty negative_selectors) then
+               "test & !(" ^ (String.concat " | " (String_set.elements negative_selectors)) ^ ")"
+             else
+               "test"
+           in
+           String_map.add dep pin acc
+         | None ->
+           let pin = "test & (" ^ (String.concat " | " v) ^ ")" in
+           String_map.add dep pin acc)
+    pds_conf.Generate_io.Pds_conf.tests_deps
+    pins
+
 let generate_pkg
     opam_dir
     (tag_opt : string option)

          
@@ 503,9 724,10 @@ let generate_pkg
     pds_conf
     test_deps_as_regular_deps =
   let tag = maybe_load_tag tag_opt in
-  let pins = maybe_load_pins pins_file_opt in
   let hll_conf = Generate_io.Hll_conf.read hll_conf in
-  let pds_conf = Generate_io.Pds_conf.read test_deps_as_regular_deps pds_conf in
+  let selector_map = hll_conf.Generate_io.Hll_conf.selector_map in
+  let pds_conf = Generate_io.Pds_conf.read test_deps_as_regular_deps pds_conf selector_map in
+  let pins = merge_pins_with_selector_deps (maybe_load_pins pins_file_opt) pds_conf in
   let gen_t =
     Generate.({ tag = tag
               ; url_protocol = hll_conf.Generate_io.Hll_conf.url_protocol