FIX Handle timezones correctly
M .merlin +2 -0
@@ 4,6 4,8 @@ B build/release/pds_template
 B build/release/snabela
 PKG cmdliner
 PKG containers
+PKG logs
+PKG logs.fmt
 PKG ppx_deriving
 PKG ppx_deriving.eq
 PKG ppx_deriving.show

          
M Makefile +3 -0
@@ 3,6 3,9 @@ 
 
 all: release
 
+.merlin: pds.conf
+	pds -f | merlin-of-pds > .merlin
+
 prepare-for-release: release
 	rm src/pds_template/pds_template.ml
 	./build/release/pds/pds.native

          
M README.md +118 -110
@@ 2,64 2,65 @@ 
 
 # Table of Contents
 
-1.  [About](#org4dc0d9e)
-    1.  [Why Does This Exist?](#org41a5fb5)
-2.  [Parallelism](#org2258f2d)
-3.  [Content Hash Based Builds](#org8a6351a)
-4.  [Terminology](#orga794655)
-5.  [Projects](#org967e68b)
-    1.  [Ocaml](#orgfb424a7)
-    2.  [Third-party](#orgc02f0aa)
-6.  [Dependencies](#org85cb810)
-7.  [Configuration](#org29323c2)
-    1.  [Configuration Variables For Projects](#org19b641a)
-    2.  [Configuration Variables For Tests](#org13fd860)
-    3.  [Outputting the configuration](#orge541245)
-8.  [Produced Files](#org8817d43)
-9.  [Documentation](#org52ed257)
-10. [Selectors](#org3c7801f)
-11. [Changelog](#orgca16c02)
-    1.  [1.0.0](#orgdcbf21c)
-    2.  [1.1.0](#orge4f6afb)
-    3.  [2.0.0](#org4776ce2)
-    4.  [3.0.0](#orgd59b0df)
-    5.  [3.0.1](#org2ee99b2)
-    6.  [3.0.2](#org268ddd0)
-    7.  [3.0.3](#orgd634407)
-    8.  [3.0.4](#org31942e6)
-    9.  [3.1.0](#org9522437)
-    10. [3.1.1](#org3564963)
-    11. [4.11](#orgae49dc5)
-    12. [4.12](#org37bf520)
-    13. [4.13](#org93db06e)
-    14. [5.14](#org2eb5e2c)
-    15. [5.15](#org2854eaf)
-    16. [5.16](#orgbf53e76)
-    17. [5.17 (bug release)](#orgf32538f)
-    18. [5.18](#orgb33dbbd)
-    19. [5.19](#org7cc9e29)
-    20. [6.20](#orgd981a1f)
-    21. [5.21](#org9cbf555)
-    22. [5.22](#org20b52e5)
-    23. [5.23](#orgdc58736)
-    24. [5.24](#org9b1f4d4)
-    25. [5.25](#orgaa03264)
-    26. [5.26 (bug release)](#org35de4c8)
-    27. [5.27](#orge109571)
-    28. [5.28](#org9cc2577)
-    29. [5.29](#org818774c)
-    30. [6.43](#org92322e5)
-    31. [6.44](#org7c34947)
-    32. [6.45](#orgae864ef)
-    33. [6.46](#orge7620f0)
-    34. [6.47](#org3d0beb4)
-    35. [6.48](#org896a043)
-    36. [6.49](#orge84fd1c)
-    37. [6.50](#orgaeb25de)
-    38. [6.51](#org6dc86ee)
+1.  [About](#orgb980c09)
+    1.  [Why Does This Exist?](#org56fd24d)
+2.  [Parallelism](#org81a3827)
+3.  [Content Hash Based Builds](#org7f92baf)
+4.  [Terminology](#org42d24bb)
+5.  [Projects](#org9476662)
+    1.  [Ocaml](#org1fb3a5d)
+    2.  [Third-party](#org3c89308)
+6.  [Dependencies](#org5e5df41)
+7.  [Configuration](#orgd67db70)
+    1.  [Configuration Variables For Projects](#org215ac13)
+    2.  [Configuration Variables For Tests](#orgb54c88a)
+    3.  [Outputting the configuration](#orgd97e2c1)
+8.  [Produced Files](#org4c19386)
+9.  [Documentation](#org1f0ce9f)
+10. [Selectors](#org9f09373)
+11. [Changelog](#org673756c)
+    1.  [1.0.0](#org1997bcd)
+    2.  [1.1.0](#orgd74e9a5)
+    3.  [2.0.0](#org7a866a9)
+    4.  [3.0.0](#org672d98f)
+    5.  [3.0.1](#orgc99efed)
+    6.  [3.0.2](#orgd4ebec4)
+    7.  [3.0.3](#org50cef80)
+    8.  [3.0.4](#orgaa2ae45)
+    9.  [3.1.0](#org7bf1c5c)
+    10. [3.1.1](#org33043f8)
+    11. [4.11](#org8c6e88f)
+    12. [4.12](#org5b03cc2)
+    13. [4.13](#org35dc9c1)
+    14. [5.14](#orgec52927)
+    15. [5.15](#orge67d5bd)
+    16. [5.16](#org2d4076c)
+    17. [5.17 (bug release)](#org58e6a49)
+    18. [5.18](#org3e619cc)
+    19. [5.19](#org93c9f01)
+    20. [6.20](#org5c202e5)
+    21. [5.21](#orgb20adf2)
+    22. [5.22](#orge87db73)
+    23. [5.23](#org18b9b45)
+    24. [5.24](#org35c90dc)
+    25. [5.25](#orgcd22eea)
+    26. [5.26 (bug release)](#org5f42e3e)
+    27. [5.27](#org0f1562c)
+    28. [5.28](#orge396426)
+    29. [5.29](#org03bb570)
+    30. [6.43](#org3a99c0a)
+    31. [6.44](#orgf02e9d4)
+    32. [6.45](#org6ab11f6)
+    33. [6.46](#org7b3352d)
+    34. [6.47](#orgb8a8e74)
+    35. [6.48](#org745abb1)
+    36. [6.49](#orgbefe34e)
+    37. [6.50](#orgad57571)
+    38. [6.51](#orgb80bf2c)
+    39. [6.52](#orgb02e4a8)
 
 
-<a id="org4dc0d9e"></a>
+<a id="orgb980c09"></a>
 
 # About
 

          
@@ 126,7 127,7 @@ the `build` directory, for example `buil
 `build/test-release`.
 
 
-<a id="org41a5fb5"></a>
+<a id="org56fd24d"></a>
 
 ## Why Does This Exist?
 

          
@@ 139,7 140,7 @@ the existing tooling then you should be 
 meant to be non-intrusive.
 
 
-<a id="org2258f2d"></a>
+<a id="org81a3827"></a>
 
 # Parallelism
 

          
@@ 159,7 160,7 @@ the source of the `units Foo and Bar mak
 error.
 
 
-<a id="org8a6351a"></a>
+<a id="org7f92baf"></a>
 
 # Content Hash Based Builds
 

          
@@ 179,7 180,7 @@ of the build configuration, this ensure 
 that causes a rebuild.
 
 
-<a id="orga794655"></a>
+<a id="org42d24bb"></a>
 
 # Terminology
 

          
@@ 191,7 192,7 @@ that causes a rebuild.
     `library`, this applies only to ocaml projects..
 
 
-<a id="org967e68b"></a>
+<a id="org9476662"></a>
 
 # Projects
 

          
@@ 202,7 203,7 @@ interface, which is `release`, `debug`, 
 and `clean`.
 
 
-<a id="orgfb424a7"></a>
+<a id="org1fb3a5d"></a>
 
 ## Ocaml
 

          
@@ 253,7 254,7 @@ generated.
 </table>
 
 
-<a id="orgc02f0aa"></a>
+<a id="org3c89308"></a>
 
 ## Third-party
 

          
@@ 261,7 262,7 @@ Third party projects do not have a `Make
 to have one which corresponds to the pds interface.
 
 
-<a id="org85cb810"></a>
+<a id="org5e5df41"></a>
 
 # Dependencies
 

          
@@ 272,7 273,7 @@ compiled before its dependent project an
 as a dependency in the dependents `Makefile`.
 
 
-<a id="org29323c2"></a>
+<a id="orgd67db70"></a>
 
 # Configuration
 

          
@@ 326,7 327,7 @@ An example of building the example direc
     install = false
 
 
-<a id="org19b641a"></a>
+<a id="org215ac13"></a>
 
 ## Configuration Variables For Projects
 

          
@@ 476,7 477,7 @@ 5.  `src.<project>.<option>`
 The same precedence applies to test builds.
 
 
-<a id="org13fd860"></a>
+<a id="orgb54c88a"></a>
 
 ## Configuration Variables For Tests
 

          
@@ 491,7 492,7 @@ 2.  `global.test-<build_type>.<option>`
 3.  `tests.<project>.<option>`
 
 
-<a id="orge541245"></a>
+<a id="orgd97e2c1"></a>
 
 ## Outputting the configuration
 

          
@@ 548,7 549,7 @@ output looks like the following:
 </table>
 
 
-<a id="org8817d43"></a>
+<a id="org4c19386"></a>
 
 # Produced Files
 

          
@@ 561,7 562,7 @@ ignored in the form of a `.gitignore`.
     Ocamlrules.mk.in
 
 
-<a id="org52ed257"></a>
+<a id="org1f0ce9f"></a>
 
 # Documentation
 

          
@@ 575,7 576,7 @@ also be set for `ocamldoc` by modifying 
 `extra_makefile_lines`.
 
 
-<a id="org3c7801f"></a>
+<a id="org9f09373"></a>
 
 # Selectors
 

          
@@ 619,12 620,12 @@ The order of precedence is that the sele
 checked, then the selector for a release.
 
 
-<a id="orgca16c02"></a>
+<a id="org673756c"></a>
 
 # Changelog
 
 
-<a id="orgdcbf21c"></a>
+<a id="org1997bcd"></a>
 
 ## 1.0.0
 

          
@@ 633,7 634,7 @@ checked, then the selector for a release
 -   Support running tests.
 
 
-<a id="orge4f6afb"></a>
+<a id="orgd74e9a5"></a>
 
 ## 1.1.0
 

          
@@ 642,7 643,7 @@ checked, then the selector for a release
     is useful if a tool needs to be built that will be used to compile a project.
 
 
-<a id="org4776ce2"></a>
+<a id="org7a866a9"></a>
 
 ## 2.0.0
 

          
@@ 651,7 652,7 @@ checked, then the selector for a release
 -   Fill out the documentation more.
 
 
-<a id="orgd59b0df"></a>
+<a id="org672d98f"></a>
 
 ## 3.0.0
 

          
@@ 659,7 660,7 @@ checked, then the selector for a release
     understands both installing and uninstalling projects.
 
 
-<a id="org2ee99b2"></a>
+<a id="orgc99efed"></a>
 
 ## 3.0.1
 

          
@@ 667,14 668,14 @@ checked, then the selector for a release
     after using hll to make the package.
 
 
-<a id="org268ddd0"></a>
+<a id="orgd4ebec4"></a>
 
 ## 3.0.2
 
 -   Expand the hll config to pass opam-linter.
 
 
-<a id="orgd634407"></a>
+<a id="org50cef80"></a>
 
 ## 3.0.3
 

          
@@ 682,7 683,7 @@ checked, then the selector for a release
 -   Specify which version of ocaml pds works with.
 
 
-<a id="org31942e6"></a>
+<a id="orgaa2ae45"></a>
 
 ## 3.0.4
 

          
@@ 690,7 691,7 @@ checked, then the selector for a release
 -   Remove examples.
 
 
-<a id="org9522437"></a>
+<a id="org7bf1c5c"></a>
 
 ## 3.1.0
 

          
@@ 698,14 699,14 @@ checked, then the selector for a release
 -   Install `.cmi` files.
 
 
-<a id="org3564963"></a>
+<a id="org33043f8"></a>
 
 ## 3.1.1
 
 -   Fix a bug in generating docs when the project has no dependencies.
 
 
-<a id="orgae49dc5"></a>
+<a id="org8c6e88f"></a>
 
 ## 4.11
 

          
@@ 716,21 717,21 @@ checked, then the selector for a release
     name of the output.
 
 
-<a id="org37bf520"></a>
+<a id="org5b03cc2"></a>
 
 ## 4.12
 
 -   Include tests directories in formatted output.
 
 
-<a id="org93db06e"></a>
+<a id="org35dc9c1"></a>
 
 ## 4.13
 
 -   Make a better error message for the install key, which is required.
 
 
-<a id="org2eb5e2c"></a>
+<a id="orgec52927"></a>
 
 ## 5.14
 

          
@@ 740,7 741,7 @@ checked, then the selector for a release
 -   Remove PACK support.  Building PACKs is no longer supported.
 
 
-<a id="org2854eaf"></a>
+<a id="orge67d5bd"></a>
 
 ## 5.15
 

          
@@ 748,14 749,14 @@ checked, then the selector for a release
 -   Add a changelog and back fill it.
 
 
-<a id="orgbf53e76"></a>
+<a id="org2d4076c"></a>
 
 ## 5.16
 
 -   Fix bug in cleanup logic which concatenated multiple deps
 
 
-<a id="orgf32538f"></a>
+<a id="org58e6a49"></a>
 
 ## 5.17 (bug release)
 

          
@@ 766,7 767,7 @@ checked, then the selector for a release
     first then the .cmo and .cmx are generated.
 
 
-<a id="orgb33dbbd"></a>
+<a id="org3e619cc"></a>
 
 ## 5.18
 

          
@@ 779,7 780,7 @@ checked, then the selector for a release
     it.  This solves it by serializing the build for those cases.
 
 
-<a id="org7cc9e29"></a>
+<a id="org93c9f01"></a>
 
 ## 5.19
 

          
@@ 787,14 788,14 @@ checked, then the selector for a release
     installs `.cmi` files for all `.ml` files.
 
 
-<a id="orgd981a1f"></a>
+<a id="org5c202e5"></a>
 
 ## 6.20
 
 -   Removed the `-custom` option from default byte code builds.
 
 
-<a id="org9cbf555"></a>
+<a id="orgb20adf2"></a>
 
 ## 5.21
 

          
@@ 802,7 803,7 @@ checked, then the selector for a release
     them to be overridden.
 
 
-<a id="org20b52e5"></a>
+<a id="orge87db73"></a>
 
 ## 5.22
 

          
@@ 810,28 811,28 @@ checked, then the selector for a release
 -   Tests compile with `OCAML*_LINK_OPTS` just like regular builds.
 
 
-<a id="orgdc58736"></a>
+<a id="org18b9b45"></a>
 
 ## 5.23
 
 -   Fix typo in PARALLEL
 
 
-<a id="org9b1f4d4"></a>
+<a id="org35c90dc"></a>
 
 ## 5.24
 
 -   Build cmti files and install them for every library.
 
 
-<a id="orgaa03264"></a>
+<a id="orgcd22eea"></a>
 
 ## 5.25
 
 -   Support adding linkopts to the META file.
 
 
-<a id="org35de4c8"></a>
+<a id="org5f42e3e"></a>
 
 ## 5.26 (bug release)
 

          
@@ 839,14 840,14 @@ checked, then the selector for a release
 -   Add some tests.
 
 
-<a id="orge109571"></a>
+<a id="org0f1562c"></a>
 
 ## 5.27
 
 -   Fix bug in section name for the selector.
 
 
-<a id="org9cc2577"></a>
+<a id="orge396426"></a>
 
 ## 5.28
 

          
@@ 854,7 855,7 @@ checked, then the selector for a release
     were not properly looked up.
 
 
-<a id="org818774c"></a>
+<a id="org03bb570"></a>
 
 ## 5.29
 

          
@@ 863,56 864,56 @@ checked, then the selector for a release
     worked).
 
 
-<a id="org92322e5"></a>
+<a id="org3a99c0a"></a>
 
 ## 6.43
 
 -   Move to content based hashing.
 
 
-<a id="org7c34947"></a>
+<a id="orgf02e9d4"></a>
 
 ## 6.44
 
 -   Fix install directive
 
 
-<a id="orgae864ef"></a>
+<a id="org6ab11f6"></a>
 
 ## 6.45
 
 -   Better sqlite error reporting
 
 
-<a id="orge7620f0"></a>
+<a id="org7b3352d"></a>
 
 ## 6.46
 
 -   strftime usage that works on Alpine
 
 
-<a id="org3d0beb4"></a>
+<a id="orgb8a8e74"></a>
 
 ## 6.47
 
 -   Another instance of strftime
 
 
-<a id="org896a043"></a>
+<a id="org745abb1"></a>
 
 ## 6.48
 
 -   Fix bugs in testing outputs.
 
 
-<a id="orge84fd1c"></a>
+<a id="orgbefe34e"></a>
 
 ## 6.49
 
 -   Improve performance 10x by batching database operations.
 
 
-<a id="orgaeb25de"></a>
+<a id="orgad57571"></a>
 
 ## 6.50
 

          
@@ 920,9 921,16 @@ checked, then the selector for a release
     that its hash had been changed, when in reality it didn't exist.
 
 
-<a id="org6dc86ee"></a>
+<a id="orgb80bf2c"></a>
 
 ## 6.51
 
 -   Remove target files from build dir when a file is deleted
 
+
+<a id="orgb02e4a8"></a>
+
+## 6.52
+
+-   Fix timezone issues in managing file modification times
+

          
M README.org +2 -0
@@ 437,3 437,5 @@ checked, then the selector for a release
   that its hash had been changed, when in reality it didn't exist.
 ** 6.51
 - Remove target files from build dir when a file is deleted
+** 6.52
+- Fix timezone issues in managing file modification times

          
M build/debug/pds/Makefile +1 -1
@@ 6,7 6,7 @@ OCAMLOPT_OPTS=$(OCAMLC_OPTS)
 
 CAMLP4=
 SRC_DIR=../../../src/pds
-PACKAGES=cmdliner containers pds_template process snabela sqlite3 toml
+PACKAGES=cmdliner containers logs logs.fmt pds_template process snabela sqlite3 toml
 LIB_MODULES=
 NON_LIB_MODULES=pds.ml 
 EXTERNAL_DEPS=../pds_template/pds_template.cma ../pds_template/pds_template.cmxa ../snabela/snabela.cma ../snabela/snabela.cmxa 

          
M build/profile/pds/Makefile +1 -1
@@ 6,7 6,7 @@ OCAMLOPT_OPTS=$(OCAMLC_OPTS)
 
 CAMLP4=
 SRC_DIR=../../../src/pds
-PACKAGES=cmdliner containers pds_template process snabela sqlite3 toml
+PACKAGES=cmdliner containers logs logs.fmt pds_template process snabela sqlite3 toml
 LIB_MODULES=
 NON_LIB_MODULES=pds.ml 
 EXTERNAL_DEPS=../pds_template/pds_template.cma ../pds_template/pds_template.cmxa ../snabela/snabela.cma ../snabela/snabela.cmxa 

          
M build/release/pds/Makefile +1 -1
@@ 6,7 6,7 @@ OCAMLOPT_OPTS=$(OCAMLC_OPTS)
 
 CAMLP4=
 SRC_DIR=../../../src/pds
-PACKAGES=cmdliner containers pds_template process snabela sqlite3 toml
+PACKAGES=cmdliner containers logs logs.fmt pds_template process snabela sqlite3 toml
 LIB_MODULES=
 NON_LIB_MODULES=pds.ml 
 EXTERNAL_DEPS=../pds_template/pds_template.cma ../pds_template/pds_template.cmxa ../snabela/snabela.cma ../snabela/snabela.cmxa 

          
M hll.conf +1 -0
@@ 17,3 17,4 @@ deps_blacklist = ["pds"]
 [deps_map]
 ppx_deriving = ["ppx_deriving.show", "ppx_deriving.eq"]
 sedlex = ["sedlex.ppx"]
+fmt = ["logs.fmt"]
  No newline at end of file

          
M pds.conf +11 -1
@@ 1,7 1,17 @@ 
 # pds recognizes two types of sections, a "src" section and
 # a "tests" section.
 [src.pds]
-deps = ["toml", "cmdliner", "process", "pds_template", "containers", "snabela", "sqlite3"]
+deps = [
+  "cmdliner",
+  "containers",
+  "logs",
+  "logs.fmt",
+  "pds_template",
+  "process",
+  "snabela",
+  "sqlite3",
+  "toml",
+]
 install = true
 # When the type is exec, how to install the executable must
 # be specified.  A library will always be installed with

          
M pds.mk +11 -9
@@ 31,12 31,12 @@ projects_install = $(RELEASE_PROJECTS:%=
 projects_docs = $(RELEASE_PROJECTS:%=docs_%)
 projects_remove = $(RELEASE_PROJECTS:%=remove_%)
 
-test_release = $(RELEASE_PROJECTS:%=test-release_%)
-test_clean_release = $(RELEASE_PROJECTS:%=test-clean-release_%)
-test_debug = $(DEBUG_PROJECTS:%=test-debug_%)
-test_clean_debug = $(DEBUG_PROJECTS:%=test-clean-debug_%)
-test_profile = $(PROFILE_PROJECTS:%=test-profile_%)
-test_clean_profile = $(PROFILE_PROJECTS:%=test-clean-profile_%)
+tests_release = $(RELEASE_TESTS:%=test-release_%)
+tests_clean_release = $(RELEASE_TESTS:%=test-clean-release_%)
+tests_debug = $(DEBUG_TESTS:%=test-debug_%)
+tests_clean_debug = $(DEBUG_TESTS:%=test-clean-debug_%)
+tests_profile = $(PROFILE_TESTS:%=test-profile_%)
+tests_clean_profile = $(PROFILE_TESTS:%=test-clean-profile_%)
 
 all: release
 

          
@@ 71,6 71,8 @@ profile_snabela:
 test-profile: $(tests_profile)
 
 
+test: test-release
+
 install: $(projects_install)
 
 docs: $(projects_docs)

          
@@ 88,19 90,19 @@ clean: $(tests_clean_release) $(tests_cl
 	$(MAKE) \
 	OCAMLPATH=$(shell pwd)/build/release:$(OCAMLPATH) \
 	BUILD_DIR=$(shell pwd)/build/release/$(patsubst release_%,%,$@) \
-	-C build/release/$(patsubst release_%,%,$@)
+	-C build/release/$(patsubst release_%,%,$@) release
 
 $(projects_debug):
 	$(MAKE) \
 	OCAMLPATH=$(shell pwd)/build/debug:$(OCAMLPATH) \
 	BUILD_DIR=$(shell pwd)/build/debug/$(patsubst debug_%,%,$@) \
-	-C build/debug/$(patsubst debug_%,%,$@)
+	-C build/debug/$(patsubst debug_%,%,$@) debug
 
 $(projects_profile):
 	$(MAKE) \
 	OCAMLPATH=$(shell pwd)/build/profile:$(OCAMLPATH) \
 	BUILD_DIR=$(shell pwd)/build/profile/$(patsubst profile_%,%,$@) \
-	-C build/profile/$(patsubst profile_%,%,$@)
+	-C build/profile/$(patsubst profile_%,%,$@) profile
 
 
 $(projects_install): $(projects_release)

          
M src/pds/pds.ml +78 -22
@@ 2,6 2,11 @@ module String_map = Map.Make (String)
 module String_set = Set.Make (String)
 module Build_set = Set.Make (String)
 module File_set = Set.Make (String)
+module Std_logs = Logs
+
+let src = Logs.Src.create "pds"
+
+module Logs = (val Logs.src_log src : Logs.LOG)
 
 let pds_mk_tmpl = "pds.mk.tmpl"
 let build_makefile_tmpl = "build_Makefile.tmpl"

          
@@ 20,6 25,10 @@ module Cmdline = struct
   let format =
     let doc = "Output a tab representation of the config file." in
     C.Arg.(value & flag & info [ "f"; "format" ] ~doc)
+
+  let debug =
+    let doc = "Enable debug logging." in
+    C.Arg.(value & flag & info [ "d"; "debug" ] ~doc)
 end
 
 module Build = struct

          
@@ 148,6 157,7 @@ module Build = struct
         | [] -> failwith "Selector produced empty output"
         | [ selector; "" ] | [ selector ] ->
             (* TODO Verify the selector has a valid name *)
+            Logs.debug (fun m -> m "selector : %s" selector);
             Some selector
         | _ -> failwith "Selector produced more than one line of output.")
     | Some [] -> failwith "Selector cannot be an empty list."

          
@@ 358,18 368,23 @@ module Hash_db = struct
   let db_path = Filename.concat "build" "pds.db"
 
   let cleanup_files build_type build_name fname =
+    Logs.debug (fun m -> m "cleanup_files");
     let build_dir = Filename.concat "build" @@ Filename.concat build_type build_name in
-    let root_fname = (Filename.chop_extension @@ Filename.basename fname) ^ "." in
+    let fname_matcher =
+      try
+        let root_fname = (Filename.chop_extension @@ Filename.basename fname) ^ "." in
+        String.starts_with ~prefix:root_fname
+      with Invalid_argument _ -> String.equal fname
+    in
     if Sys.is_directory build_dir then
-      let files =
-        Sys.readdir build_dir
-        |> Array.to_list
-        |> CCList.filter (String.starts_with ~prefix:root_fname)
-      in
+      let files = Sys.readdir build_dir |> Array.to_list |> CCList.filter fname_matcher in
       CCList.iter
         (fun name ->
           let fname = Filename.concat build_dir name in
-          if Sys.file_exists fname then Sys.remove fname)
+          Logs.debug (fun m -> m "exists? : %s" fname);
+          if Sys.file_exists fname then (
+            Logs.info (fun m -> m "deleting : %s" fname);
+            Sys.remove fname))
         files
 
   let remove_deleted_files srcs tests paths =

          
@@ 437,10 452,10 @@ module Hash_db = struct
     @@ Sqlite3.exec
          db
          "create table if not exists hashes (path text primary key, hash text, modified_at text)";
+    assert_rc @@ Sqlite3.exec db "drop table if exists last_run";
     assert_rc
-    @@ Sqlite3.exec
-         db
-         "create table if not exists last_run (path text primary key, hash text, modified_at text)";
+    @@ Sqlite3.exec db "create table last_run (path text primary key, hash text, modified_at text)";
+    Logs.debug (fun m -> m "calculating hashes and modified at");
     let path_hashes_modified =
       (List.map (fun (path, hash) ->
            let hash =

          
@@ 450,13 465,14 @@ module Hash_db = struct
            (path, hash, modified_at)))
         path_hashes
     in
+    Logs.debug (fun m -> m "inserting latest runs");
     let query =
       "insert or replace into last_run (path, hash, modified_at) values "
       ^ String.concat
           ", "
           (CCList.replicate
              (List.length path_hashes_modified)
-             "(?, ?, strftime('%Y-%m-%dT%H:%M:%S', ?, 'unixepoch'))")
+             "(?, ?, strftime('%Y-%m-%dT%H:%M:%S', ?, 'unixepoch', 'localtime'))")
     in
     let stmt = Sqlite3.prepare db query in
     List.iteri

          
@@ 467,27 483,37 @@ module Hash_db = struct
         ())
       path_hashes_modified;
     assert_rc @@ Sqlite3.step stmt;
+    assert_rc @@ Sqlite3.finalize stmt;
+    Logs.debug (fun m -> m "finding same hashes with modified date");
     let stmt =
       Sqlite3.prepare
         db
-        "select lr.path, hashes.modified_at from last_run as lr inner join hashes on hashes.path = \
-         lr.path where hashes.hash = lr.hash and lr.modified_at <> hashes.modified_at"
+        "select lr.path, hashes.modified_at, lr.modified_at from last_run as lr inner join hashes \
+         on hashes.path = lr.path where hashes.hash = lr.hash and lr.modified_at <> \
+         hashes.modified_at"
     in
     let rc, rows =
       Sqlite3.fold
         stmt
         ~f:(fun acc row ->
           match row with
-          | Sqlite3.Data.[| TEXT path; TEXT modified_at |] -> (path, modified_at) :: acc
+          | Sqlite3.Data.[| TEXT path; TEXT hashes_modified_at; TEXT lr_modified_at |] ->
+              (path, hashes_modified_at, lr_modified_at) :: acc
           | _ -> assert false)
         ~init:[]
     in
     assert_rc rc;
     List.iter
-      (fun (path, modified_at) ->
-        ignore (Unix.system (sprintf "touch -d '%s' %S" modified_at path)))
+      (fun (path, hashes_modified_at, lr_modified_at) ->
+        Logs.info (fun m -> m "setting time : %s : %s -> %s" path lr_modified_at hashes_modified_at);
+        Logs.debug (fun m -> m "touch -d '%s' %S" hashes_modified_at path);
+        ignore (Unix.system (sprintf "touch -d '%s' %S" hashes_modified_at path)))
       rows;
-    let stmt = Sqlite3.prepare db "select strftime('%Y-%m-%dT%H:%M:%S')" in
+    let stmt =
+      Sqlite3.prepare
+        db
+        "select strftime('%Y-%m-%dT%H:%M:%S', unixepoch(), 'unixepoch', 'localtime')"
+    in
     let rc, rows =
       Sqlite3.fold
         stmt

          
@@ 503,7 529,9 @@ module Hash_db = struct
       | datetime :: _ -> datetime
       | [] -> assert false
     in
+    Logs.info (fun m -> m "datetime: %s" datetime);
     (* Set the date on any changed files. *)
+    Logs.debug (fun m -> m "finding modified hashes");
     let stmt =
       Sqlite3.prepare
         db

          
@@ 520,12 548,25 @@ module Hash_db = struct
         ~init:[]
     in
     assert_rc rc;
-    if rows <> [] then
+    if rows <> [] then (
+      Logs.info (fun m -> List.iter (m "setting time : %s : %s" datetime) rows);
+      let query =
+        "update last_run set modified_at = ? where "
+        ^ String.concat " or " (List.map (fun _ -> "path = ?") rows)
+      in
+      let stmt = Sqlite3.prepare db query in
+      assert_rc @@ Sqlite3.bind_text stmt 1 datetime;
+      List.iteri (fun idx path -> assert_rc @@ Sqlite3.bind_text stmt (2 + idx) path) rows;
+      assert_rc @@ Sqlite3.step stmt;
+      assert_rc @@ Sqlite3.finalize stmt;
+      Logs.debug (fun m ->
+          m "touch -d '%s' %s" datetime (String.concat " " (List.map (sprintf "%S") rows)));
       ignore
         (Unix.system
-           (sprintf "touch -d '%s' " datetime ^ String.concat " " (List.map (sprintf "%S") rows)));
+           (sprintf "touch -d '%s' " datetime ^ String.concat " " (List.map (sprintf "%S") rows))));
     (* Delete the build artifacts of any missing files.  We assume the build
        artifacts start with the same name.  *)
+    Logs.debug (fun m -> m "finding removed files");
     let stmt =
       Sqlite3.prepare
         db

          
@@ 543,8 584,17 @@ module Hash_db = struct
     in
     assert_rc rc;
     if rows <> [] then remove_deleted_files srcs tests rows;
-    (* Rename last run to hashes *)
+    (* Move files with different hashes into hashes table. *)
+    Logs.debug (fun m -> m "set modified_at to in last_run to hashes for matching file hashes");
+    assert_rc
+    @@ Sqlite3.exec
+         db
+         "insert or replace into last_run (path, hash, modified_at) select hashes.path, \
+          hashes.hash, hashes.modified_at from hashes inner join last_run as lr on hashes.path = \
+          lr.path where hashes.hash = lr.hash and hashes.modified_at <> lr.modified_at";
+    Logs.debug (fun m -> m "drop hashes");
     assert_rc @@ Sqlite3.exec db "drop table hashes";
+    Logs.debug (fun m -> m "rename last_run to hashes");
     assert_rc @@ Sqlite3.exec db "alter table last_run rename to hashes";
     ignore (Sqlite3.db_close db);
     ()

          
@@ 913,7 963,11 @@ let emit_formatted pds_conf =
   emit_formatted_builds builds;
   emit_formatted_tests builds
 
-let prepare_build = function
+let prepare_build debug =
+  Std_logs.set_reporter (Logs_fmt.reporter ());
+  if debug then Std_logs.set_level (Some Std_logs.Debug)
+  else Std_logs.set_level (Some Std_logs.Error);
+  function
   | true ->
       (* Format *)
       emit_formatted "pds.conf"

          
@@ 924,7 978,9 @@ let prepare_build = function
 let cmd =
   let doc = "Emit build configs" in
   Cmdliner.(
-    Cmd.v (Cmd.info "pds" ~doc ~exits:Cmd.Exit.defaults) Term.(const prepare_build $ Cmdline.format))
+    Cmd.v
+      (Cmd.info "pds" ~doc ~exits:Cmd.Exit.defaults)
+      Term.(const prepare_build $ Cmdline.debug $ Cmdline.format))
 
 let main () = exit @@ Cmdliner.Cmd.eval cmd
 let () = main ()