Imported Upstream version 3.3 upstream/3.3
authorJinWang An <jinwang.an@samsung.com>
Tue, 3 Aug 2021 07:17:48 +0000 (16:17 +0900)
committerJinWang An <jinwang.an@samsung.com>
Tue, 3 Aug 2021 07:17:48 +0000 (16:17 +0900)
69 files changed:
AUTHORS.html
AUTHORS.txt
HACKING.txt
INSTALL.html [deleted file]
INSTALL.md [new file with mode: 0644]
INSTALL.txt [deleted file]
LICENSE.html
LICENSE.txt
MANUAL.html
MANUAL.txt
Makefile.in
NEWS.html
NEWS.txt
README.html [deleted file]
README.md [moved from README.txt with 52% similarity]
args.c
ccache.1
ccache.c
ccache.h
cleanup.c
compopt.c
compopt.h
conf.c
conf.h
config.h.in
configure
configure.ac
confitems.gperf
confitems_lookup.c
counters.c
counters.h
dev.mk.in
envtoconfitems.gperf
envtoconfitems_lookup.c
execute.c
exitfn.c
hash.c
hashutil.c
language.c
language.h
lockfile.c
macroskip.h
main.c
manifest.c
mdfour.c
murmurhashneutral2.c
snprintf.c
stats.c
system.h
test.sh
test/framework.c
test/framework.h
test/main.c
test/test_args.c
test/test_argument_processing.c
test/test_compopt.c
test/test_conf.c
test/test_counters.c
test/test_hash.c
test/test_hashutil.c
test/test_lockfile.c
test/test_stats.c
test/test_util.c
test/util.c
test/util.h
unify.c
util.c
version.c
zlib/inflate.c

index fec48b6..9d018bc 100644 (file)
@@ -734,7 +734,7 @@ asciidoc.install(2);
 <body class="article">\r
 <div id="header">\r
 <h1>ccache authors</h1>\r
-<span id="revnumber">version 3.2.9</span>\r
+<span id="revnumber">version 3.3</span>\r
 <div id="toc">
   <div id="toctitle">Table of Contents</div>
   <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
@@ -779,6 +779,11 @@ Andrew Tridgell &lt;<a href="mailto:tridge@samba.org">tridge@samba.org</a>&gt;
 </li>\r
 <li>\r
 <p>\r
+AndrĂ© Klitzing &lt;<a href="mailto:aklitzing@gmail.com">aklitzing@gmail.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 Bernhard Bauer &lt;<a href="mailto:bauerb@chromium.org">bauerb@chromium.org</a>&gt;\r
 </p>\r
 </li>\r
@@ -804,7 +809,7 @@ Chris AtLee &lt;<a href="mailto:chris@atlee.ca">chris@atlee.ca</a>&gt;
 </li>\r
 <li>\r
 <p>\r
-Clemens Rabe &lt;<a href="mailto:crabe@gmx.de">crabe@gmx.de</a>&gt;\r
+Clemens Rabe &lt;<a href="mailto:clemens.rabe@gmail.com">clemens.rabe@gmail.com</a>&gt;\r
 </p>\r
 </li>\r
 <li>\r
@@ -829,6 +834,11 @@ Hongli Lai &lt;<a href="mailto:hongli@phusion.nl">hongli@phusion.nl</a>&gt;
 </li>\r
 <li>\r
 <p>\r
+Ivan Vaigult &lt;<a href="mailto:i.vaigult@gmail.com">i.vaigult@gmail.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 Jiang Jiang &lt;<a href="mailto:jiangj@opera.com">jiangj@opera.com</a>&gt;\r
 </p>\r
 </li>\r
@@ -839,6 +849,11 @@ Joel Rosdahl &lt;<a href="mailto:joel@rosdahl.net">joel@rosdahl.net</a>&gt;
 </li>\r
 <li>\r
 <p>\r
+John Basila &lt;<a href="mailto:jbasila@checkpoint.com">jbasila@checkpoint.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 John Coiner &lt;<a href="mailto:john.coiner@amd.com">john.coiner@amd.com</a>&gt;\r
 </p>\r
 </li>\r
@@ -909,6 +924,11 @@ Matthias Kretz &lt;<a href="mailto:kretz@kde.org">kretz@kde.org</a>&gt;
 </li>\r
 <li>\r
 <p>\r
+Melven Roehrig-Zoellner &lt;<a href="mailto:Melven.Roehrig-Zoellner@DLR.de">Melven.Roehrig-Zoellner@DLR.de</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 Michael Marineau &lt;<a href="mailto:michael.marineau@coreos.com">michael.marineau@coreos.com</a>&gt;\r
 </p>\r
 </li>\r
@@ -919,6 +939,11 @@ Michael Meeks &lt;<a href="mailto:michael.meeks@suse.com">michael.meeks@suse.com
 </li>\r
 <li>\r
 <p>\r
+Mihai Serban &lt;<a href="mailto:mihai.serban@intel.com">mihai.serban@intel.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 Mike Frysinger &lt;<a href="mailto:vapier@gentoo.org">vapier@gentoo.org</a>&gt;\r
 </p>\r
 </li>\r
@@ -939,11 +964,21 @@ Nick Schultz &lt;<a href="mailto:nick.schultz@intel.com">nick.schultz@intel.com<
 </li>\r
 <li>\r
 <p>\r
+Norbert Lange &lt;<a href="mailto:nolange79@gmail.com">nolange79@gmail.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 Orgad Shaneh &lt;<a href="mailto:orgad.shaneh@audiocodes.com">orgad.shaneh@audiocodes.com</a>&gt;\r
 </p>\r
 </li>\r
 <li>\r
 <p>\r
+Orion Poplawski &lt;<a href="mailto:orion@cora.nwra.com">orion@cora.nwra.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 Owen Mann &lt;<a href="mailto:owen@mann.org">owen@mann.org</a>&gt;\r
 </p>\r
 </li>\r
@@ -959,6 +994,11 @@ Paul Griffith &lt;<a href="mailto:paulg@cse.yorku.ca">paulg@cse.yorku.ca</a>&gt;
 </li>\r
 <li>\r
 <p>\r
+Pavel Boldin &lt;<a href="mailto:pboldin@cloudlinux.com">pboldin@cloudlinux.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 RW &lt;<a href="mailto:fbsd06@mlists.homeunix.com">fbsd06@mlists.homeunix.com</a>&gt;\r
 </p>\r
 </li>\r
@@ -974,6 +1014,11 @@ Robin H. Johnson &lt;<a href="mailto:robbat2@gentoo.org">robbat2@gentoo.org</a>&
 </li>\r
 <li>\r
 <p>\r
+Rolf Bjarne Kvinge &lt;<a href="mailto:rolf@xamarin.com">rolf@xamarin.com</a>&gt;\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
 Ryan Brown &lt;<a href="mailto:ryb@ableton.com">ryb@ableton.com</a>&gt;\r
 </p>\r
 </li>\r
@@ -1020,9 +1065,9 @@ Yiding Jia &lt;<a href="mailto:yiding@fb.com">yiding@fb.com</a>&gt;
 <div id="footnotes"><hr /></div>\r
 <div id="footer">\r
 <div id="footer-text">\r
-Version 3.2.9<br />\r
+Version 3.3<br />\r
 Last updated\r
- 2016-09-28 21:28:19 CEST\r
+ 2016-08-27 16:54:44 CEST\r
 </div>\r
 </div>\r
 </body>\r
index 2aedab2..f6dffa4 100644 (file)
@@ -12,18 +12,21 @@ ccache is a collective work with contributions from many people, including:
 * Andrew P Boie <andrew.p.boie@intel.com>
 * Andrew Stubbs <ams@codesourcery.com>
 * Andrew Tridgell <tridge@samba.org>
+* AndrĂ© Klitzing <aklitzing@gmail.com>
 * Bernhard Bauer <bauerb@chromium.org>
 * Björn Jacke <bj@sernet.de>
 * Bo Rydberg <bolry@hotmail.com>
 * Chiaki Ishikawa <ishikawa@yk.rim.or.jp>
 * Chris AtLee <chris@atlee.ca>
-* Clemens Rabe <crabe@gmx.de>
+* Clemens Rabe <clemens.rabe@gmail.com>
 * David Givone <david@givone.net>
 * Eric Blau <Eric.Blau@tekelec.com>
 * Francois Marier <francois@debian.org>
 * Hongli Lai <hongli@phusion.nl>
+* Ivan Vaigult <i.vaigult@gmail.com>
 * Jiang Jiang <jiangj@opera.com>
 * Joel Rosdahl <joel@rosdahl.net>
+* John Basila <jbasila@checkpoint.com>
 * John Coiner <john.coiner@amd.com>
 * Jon Bernard <jbernard@tuxion.com>
 * Justin Lebar <justin.lebar@gmail.com>
@@ -38,19 +41,25 @@ ccache is a collective work with contributions from many people, including:
 * Mark Starovoytov <starovoytov.mark@googlemail.com>
 * Martin Pool <mbp@sourcefrog.net>
 * Matthias Kretz <kretz@kde.org>
+* Melven Roehrig-Zoellner <Melven.Roehrig-Zoellner@DLR.de>
 * Michael Marineau <michael.marineau@coreos.com>
 * Michael Meeks <michael.meeks@suse.com>
+* Mihai Serban <mihai.serban@intel.com>
 * Mike Frysinger <vapier@gentoo.org>
 * Mikhail Kolomeytsev <mkolom@yandex-team.ru>
 * Neil Mushell <nmushell@bloomberg.net>
 * Nick Schultz <nick.schultz@intel.com>
+* Norbert Lange <nolange79@gmail.com>
 * Orgad Shaneh <orgad.shaneh@audiocodes.com>
+* Orion Poplawski <orion@cora.nwra.com>
 * Owen Mann <owen@mann.org>
 * Patrick von Reth <vonreth@kde.org>
 * Paul Griffith <paulg@cse.yorku.ca>
+* Pavel Boldin <pboldin@cloudlinux.com>
 * RW <fbsd06@mlists.homeunix.com>
 * Ramiro Polla <ramiro.polla@gmail.com>
 * Robin H. Johnson <robbat2@gentoo.org>
+* Rolf Bjarne Kvinge <rolf@xamarin.com>
 * Ryan Brown <ryb@ableton.com>
 * Tim Potter <tpot@samba.org>
 * Tor Arne Vestbø <torarnv@gmail.com>
index e025c11..50ed26f 100644 (file)
@@ -17,6 +17,7 @@ Code formatting
 * Use only lowercase names for functions and variables.
 * Use only uppercase names for enum items and (with some exceptions) macros.
 * Don't use typedefs for structs and enums.
+* Use //-style comments.
 
 Tip: Install the tool uncrustify <http://uncrustify.sourceforge.net> and then
 run "make uncrustify" to fix up source code formatting.
@@ -24,6 +25,8 @@ run "make uncrustify" to fix up source code formatting.
 Idioms
 ------
 
+* Declare variables as late as convenient, not necessarily at the beginning of
+  the scope.
 * Use NULL to initialize null pointers.
 * Don't use NULL when comparing pointers.
 * Use format(), x_malloc() and friends instead of checking for memory
@@ -43,21 +46,29 @@ Other
 Commit messages
 ---------------
 
-* Write a short description on the first line. If wanted, leave the second line
-  empty and write a longer description on line three and below.
-* Start the short description with a capital letter. Optional: prefix the short
+* Write a summary (short description) on the first line.
+* Start the summary with a capital letter. Optional: prefix the short
   description with a context followed by a colon.
-* The short description should be in "command form" (see examples below).
-* Don't put a final period after the short description.
-* Keep lines in the message at most 75 characters wide.
+* The summary should be in imperative mood (see examples below).
+* The summary should not end with a period. It's a title and titles don't end
+  with a period.
+* If a longer description is wanted, add a second line empty and write the
+  longer description on line three and below.
+* Keep lines in the message at most 72 characters wide.
 
 Example 1:
 
     Hash a delimiter string between parts to separate them
 
-    Previously, "gcc -I-O2 -c file.c" and "gcc -I -O2 -c file.c" would hash to
-    the same sum.
+    Previously, "gcc -I-O2 -c file.c" and "gcc -I -O2 -c file.c" would hash
+    to the same sum.
 
 Example 2:
 
     win32: Add a space between filename and error string in x_fmmap()
+
+See also:
+
+* http://stopwritingramblingcommitmessages.com
+* http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
+* https://github.com/erlang/otp/wiki/Writing-good-commit-messages
diff --git a/INSTALL.html b/INSTALL.html
deleted file mode 100644 (file)
index 9a3eb99..0000000
+++ /dev/null
@@ -1,857 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
-    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<head>\r
-<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
-<meta name="generator" content="AsciiDoc 8.6.9" />\r
-<title>ccache installation</title>\r
-<style type="text/css">\r
-/* Shared CSS for AsciiDoc xhtml11 and html5 backends */\r
-\r
-/* Default font. */\r
-body {\r
-  font-family: Georgia,serif;\r
-}\r
-\r
-/* Title font. */\r
-h1, h2, h3, h4, h5, h6,\r
-div.title, caption.title,\r
-thead, p.table.header,\r
-#toctitle,\r
-#author, #revnumber, #revdate, #revremark,\r
-#footer {\r
-  font-family: Arial,Helvetica,sans-serif;\r
-}\r
-\r
-body {\r
-  margin: 1em 5% 1em 5%;\r
-}\r
-\r
-a {\r
-  color: blue;\r
-  text-decoration: underline;\r
-}\r
-a:visited {\r
-  color: fuchsia;\r
-}\r
-\r
-em {\r
-  font-style: italic;\r
-  color: navy;\r
-}\r
-\r
-strong {\r
-  font-weight: bold;\r
-  color: #083194;\r
-}\r
-\r
-h1, h2, h3, h4, h5, h6 {\r
-  color: #527bbd;\r
-  margin-top: 1.2em;\r
-  margin-bottom: 0.5em;\r
-  line-height: 1.3;\r
-}\r
-\r
-h1, h2, h3 {\r
-  border-bottom: 2px solid silver;\r
-}\r
-h2 {\r
-  padding-top: 0.5em;\r
-}\r
-h3 {\r
-  float: left;\r
-}\r
-h3 + * {\r
-  clear: left;\r
-}\r
-h5 {\r
-  font-size: 1.0em;\r
-}\r
-\r
-div.sectionbody {\r
-  margin-left: 0;\r
-}\r
-\r
-hr {\r
-  border: 1px solid silver;\r
-}\r
-\r
-p {\r
-  margin-top: 0.5em;\r
-  margin-bottom: 0.5em;\r
-}\r
-\r
-ul, ol, li > p {\r
-  margin-top: 0;\r
-}\r
-ul > li     { color: #aaa; }\r
-ul > li > * { color: black; }\r
-\r
-.monospaced, code, pre {\r
-  font-family: "Courier New", Courier, monospace;\r
-  font-size: inherit;\r
-  color: navy;\r
-  padding: 0;\r
-  margin: 0;\r
-}\r
-pre {\r
-  white-space: pre-wrap;\r
-}\r
-\r
-#author {\r
-  color: #527bbd;\r
-  font-weight: bold;\r
-  font-size: 1.1em;\r
-}\r
-#email {\r
-}\r
-#revnumber, #revdate, #revremark {\r
-}\r
-\r
-#footer {\r
-  font-size: small;\r
-  border-top: 2px solid silver;\r
-  padding-top: 0.5em;\r
-  margin-top: 4.0em;\r
-}\r
-#footer-text {\r
-  float: left;\r
-  padding-bottom: 0.5em;\r
-}\r
-#footer-badges {\r
-  float: right;\r
-  padding-bottom: 0.5em;\r
-}\r
-\r
-#preamble {\r
-  margin-top: 1.5em;\r
-  margin-bottom: 1.5em;\r
-}\r
-div.imageblock, div.exampleblock, div.verseblock,\r
-div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
-div.admonitionblock {\r
-  margin-top: 1.0em;\r
-  margin-bottom: 1.5em;\r
-}\r
-div.admonitionblock {\r
-  margin-top: 2.0em;\r
-  margin-bottom: 2.0em;\r
-  margin-right: 10%;\r
-  color: #606060;\r
-}\r
-\r
-div.content { /* Block element content. */\r
-  padding: 0;\r
-}\r
-\r
-/* Block element titles. */\r
-div.title, caption.title {\r
-  color: #527bbd;\r
-  font-weight: bold;\r
-  text-align: left;\r
-  margin-top: 1.0em;\r
-  margin-bottom: 0.5em;\r
-}\r
-div.title + * {\r
-  margin-top: 0;\r
-}\r
-\r
-td div.title:first-child {\r
-  margin-top: 0.0em;\r
-}\r
-div.content div.title:first-child {\r
-  margin-top: 0.0em;\r
-}\r
-div.content + div.title {\r
-  margin-top: 0.0em;\r
-}\r
-\r
-div.sidebarblock > div.content {\r
-  background: #ffffee;\r
-  border: 1px solid #dddddd;\r
-  border-left: 4px solid #f0f0f0;\r
-  padding: 0.5em;\r
-}\r
-\r
-div.listingblock > div.content {\r
-  border: 1px solid #dddddd;\r
-  border-left: 5px solid #f0f0f0;\r
-  background: #f8f8f8;\r
-  padding: 0.5em;\r
-}\r
-\r
-div.quoteblock, div.verseblock {\r
-  padding-left: 1.0em;\r
-  margin-left: 1.0em;\r
-  margin-right: 10%;\r
-  border-left: 5px solid #f0f0f0;\r
-  color: #888;\r
-}\r
-\r
-div.quoteblock > div.attribution {\r
-  padding-top: 0.5em;\r
-  text-align: right;\r
-}\r
-\r
-div.verseblock > pre.content {\r
-  font-family: inherit;\r
-  font-size: inherit;\r
-}\r
-div.verseblock > div.attribution {\r
-  padding-top: 0.75em;\r
-  text-align: left;\r
-}\r
-/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
-div.verseblock + div.attribution {\r
-  text-align: left;\r
-}\r
-\r
-div.admonitionblock .icon {\r
-  vertical-align: top;\r
-  font-size: 1.1em;\r
-  font-weight: bold;\r
-  text-decoration: underline;\r
-  color: #527bbd;\r
-  padding-right: 0.5em;\r
-}\r
-div.admonitionblock td.content {\r
-  padding-left: 0.5em;\r
-  border-left: 3px solid #dddddd;\r
-}\r
-\r
-div.exampleblock > div.content {\r
-  border-left: 3px solid #dddddd;\r
-  padding-left: 0.5em;\r
-}\r
-\r
-div.imageblock div.content { padding-left: 0; }\r
-span.image img { border-style: none; vertical-align: text-bottom; }\r
-a.image:visited { color: white; }\r
-\r
-dl {\r
-  margin-top: 0.8em;\r
-  margin-bottom: 0.8em;\r
-}\r
-dt {\r
-  margin-top: 0.5em;\r
-  margin-bottom: 0;\r
-  font-style: normal;\r
-  color: navy;\r
-}\r
-dd > *:first-child {\r
-  margin-top: 0.1em;\r
-}\r
-\r
-ul, ol {\r
-    list-style-position: outside;\r
-}\r
-ol.arabic {\r
-  list-style-type: decimal;\r
-}\r
-ol.loweralpha {\r
-  list-style-type: lower-alpha;\r
-}\r
-ol.upperalpha {\r
-  list-style-type: upper-alpha;\r
-}\r
-ol.lowerroman {\r
-  list-style-type: lower-roman;\r
-}\r
-ol.upperroman {\r
-  list-style-type: upper-roman;\r
-}\r
-\r
-div.compact ul, div.compact ol,\r
-div.compact p, div.compact p,\r
-div.compact div, div.compact div {\r
-  margin-top: 0.1em;\r
-  margin-bottom: 0.1em;\r
-}\r
-\r
-tfoot {\r
-  font-weight: bold;\r
-}\r
-td > div.verse {\r
-  white-space: pre;\r
-}\r
-\r
-div.hdlist {\r
-  margin-top: 0.8em;\r
-  margin-bottom: 0.8em;\r
-}\r
-div.hdlist tr {\r
-  padding-bottom: 15px;\r
-}\r
-dt.hdlist1.strong, td.hdlist1.strong {\r
-  font-weight: bold;\r
-}\r
-td.hdlist1 {\r
-  vertical-align: top;\r
-  font-style: normal;\r
-  padding-right: 0.8em;\r
-  color: navy;\r
-}\r
-td.hdlist2 {\r
-  vertical-align: top;\r
-}\r
-div.hdlist.compact tr {\r
-  margin: 0;\r
-  padding-bottom: 0;\r
-}\r
-\r
-.comment {\r
-  background: yellow;\r
-}\r
-\r
-.footnote, .footnoteref {\r
-  font-size: 0.8em;\r
-}\r
-\r
-span.footnote, span.footnoteref {\r
-  vertical-align: super;\r
-}\r
-\r
-#footnotes {\r
-  margin: 20px 0 20px 0;\r
-  padding: 7px 0 0 0;\r
-}\r
-\r
-#footnotes div.footnote {\r
-  margin: 0 0 5px 0;\r
-}\r
-\r
-#footnotes hr {\r
-  border: none;\r
-  border-top: 1px solid silver;\r
-  height: 1px;\r
-  text-align: left;\r
-  margin-left: 0;\r
-  width: 20%;\r
-  min-width: 100px;\r
-}\r
-\r
-div.colist td {\r
-  padding-right: 0.5em;\r
-  padding-bottom: 0.3em;\r
-  vertical-align: top;\r
-}\r
-div.colist td img {\r
-  margin-top: 0.3em;\r
-}\r
-\r
-@media print {\r
-  #footer-badges { display: none; }\r
-}\r
-\r
-#toc {\r
-  margin-bottom: 2.5em;\r
-}\r
-\r
-#toctitle {\r
-  color: #527bbd;\r
-  font-size: 1.1em;\r
-  font-weight: bold;\r
-  margin-top: 1.0em;\r
-  margin-bottom: 0.1em;\r
-}\r
-\r
-div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
-  margin-top: 0;\r
-  margin-bottom: 0;\r
-}\r
-div.toclevel2 {\r
-  margin-left: 2em;\r
-  font-size: 0.9em;\r
-}\r
-div.toclevel3 {\r
-  margin-left: 4em;\r
-  font-size: 0.9em;\r
-}\r
-div.toclevel4 {\r
-  margin-left: 6em;\r
-  font-size: 0.9em;\r
-}\r
-\r
-span.aqua { color: aqua; }\r
-span.black { color: black; }\r
-span.blue { color: blue; }\r
-span.fuchsia { color: fuchsia; }\r
-span.gray { color: gray; }\r
-span.green { color: green; }\r
-span.lime { color: lime; }\r
-span.maroon { color: maroon; }\r
-span.navy { color: navy; }\r
-span.olive { color: olive; }\r
-span.purple { color: purple; }\r
-span.red { color: red; }\r
-span.silver { color: silver; }\r
-span.teal { color: teal; }\r
-span.white { color: white; }\r
-span.yellow { color: yellow; }\r
-\r
-span.aqua-background { background: aqua; }\r
-span.black-background { background: black; }\r
-span.blue-background { background: blue; }\r
-span.fuchsia-background { background: fuchsia; }\r
-span.gray-background { background: gray; }\r
-span.green-background { background: green; }\r
-span.lime-background { background: lime; }\r
-span.maroon-background { background: maroon; }\r
-span.navy-background { background: navy; }\r
-span.olive-background { background: olive; }\r
-span.purple-background { background: purple; }\r
-span.red-background { background: red; }\r
-span.silver-background { background: silver; }\r
-span.teal-background { background: teal; }\r
-span.white-background { background: white; }\r
-span.yellow-background { background: yellow; }\r
-\r
-span.big { font-size: 2em; }\r
-span.small { font-size: 0.6em; }\r
-\r
-span.underline { text-decoration: underline; }\r
-span.overline { text-decoration: overline; }\r
-span.line-through { text-decoration: line-through; }\r
-\r
-div.unbreakable { page-break-inside: avoid; }\r
-\r
-\r
-/*\r
- * xhtml11 specific\r
- *\r
- * */\r
-\r
-div.tableblock {\r
-  margin-top: 1.0em;\r
-  margin-bottom: 1.5em;\r
-}\r
-div.tableblock > table {\r
-  border: 3px solid #527bbd;\r
-}\r
-thead, p.table.header {\r
-  font-weight: bold;\r
-  color: #527bbd;\r
-}\r
-p.table {\r
-  margin-top: 0;\r
-}\r
-/* Because the table frame attribute is overriden by CSS in most browsers. */\r
-div.tableblock > table[frame="void"] {\r
-  border-style: none;\r
-}\r
-div.tableblock > table[frame="hsides"] {\r
-  border-left-style: none;\r
-  border-right-style: none;\r
-}\r
-div.tableblock > table[frame="vsides"] {\r
-  border-top-style: none;\r
-  border-bottom-style: none;\r
-}\r
-\r
-\r
-/*\r
- * html5 specific\r
- *\r
- * */\r
-\r
-table.tableblock {\r
-  margin-top: 1.0em;\r
-  margin-bottom: 1.5em;\r
-}\r
-thead, p.tableblock.header {\r
-  font-weight: bold;\r
-  color: #527bbd;\r
-}\r
-p.tableblock {\r
-  margin-top: 0;\r
-}\r
-table.tableblock {\r
-  border-width: 3px;\r
-  border-spacing: 0px;\r
-  border-style: solid;\r
-  border-color: #527bbd;\r
-  border-collapse: collapse;\r
-}\r
-th.tableblock, td.tableblock {\r
-  border-width: 1px;\r
-  padding: 4px;\r
-  border-style: solid;\r
-  border-color: #527bbd;\r
-}\r
-\r
-table.tableblock.frame-topbot {\r
-  border-left-style: hidden;\r
-  border-right-style: hidden;\r
-}\r
-table.tableblock.frame-sides {\r
-  border-top-style: hidden;\r
-  border-bottom-style: hidden;\r
-}\r
-table.tableblock.frame-none {\r
-  border-style: hidden;\r
-}\r
-\r
-th.tableblock.halign-left, td.tableblock.halign-left {\r
-  text-align: left;\r
-}\r
-th.tableblock.halign-center, td.tableblock.halign-center {\r
-  text-align: center;\r
-}\r
-th.tableblock.halign-right, td.tableblock.halign-right {\r
-  text-align: right;\r
-}\r
-\r
-th.tableblock.valign-top, td.tableblock.valign-top {\r
-  vertical-align: top;\r
-}\r
-th.tableblock.valign-middle, td.tableblock.valign-middle {\r
-  vertical-align: middle;\r
-}\r
-th.tableblock.valign-bottom, td.tableblock.valign-bottom {\r
-  vertical-align: bottom;\r
-}\r
-\r
-\r
-/*\r
- * manpage specific\r
- *\r
- * */\r
-\r
-body.manpage h1 {\r
-  padding-top: 0.5em;\r
-  padding-bottom: 0.5em;\r
-  border-top: 2px solid silver;\r
-  border-bottom: 2px solid silver;\r
-}\r
-body.manpage h2 {\r
-  border-style: none;\r
-}\r
-body.manpage div.sectionbody {\r
-  margin-left: 3em;\r
-}\r
-\r
-@media print {\r
-  body.manpage div#toc { display: none; }\r
-}\r
-\r
-\r
-</style>\r
-<script type="text/javascript">\r
-/*<![CDATA[*/\r
-var asciidoc = {  // Namespace.\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Table Of Contents generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Author: Mihai Bazon, September 2002\r
- * http://students.infoiasi.ro/~mishoo\r
- *\r
- * Table Of Content generator\r
- * Version: 0.4\r
- *\r
- * Feel free to use this script under the terms of the GNU General Public\r
- * License, as long as you do not remove or alter this notice.\r
- */\r
-\r
- /* modified by Troy D. Hanson, September 2006. License: GPL */\r
- /* modified by Stuart Rackham, 2006, 2009. License: GPL */\r
-\r
-// toclevels = 1..4.\r
-toc: function (toclevels) {\r
-\r
-  function getText(el) {\r
-    var text = "";\r
-    for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
-      if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.\r
-        text += i.data;\r
-      else if (i.firstChild != null)\r
-        text += getText(i);\r
-    }\r
-    return text;\r
-  }\r
-\r
-  function TocEntry(el, text, toclevel) {\r
-    this.element = el;\r
-    this.text = text;\r
-    this.toclevel = toclevel;\r
-  }\r
-\r
-  function tocEntries(el, toclevels) {\r
-    var result = new Array;\r
-    var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');\r
-    // Function that scans the DOM tree for header elements (the DOM2\r
-    // nodeIterator API would be a better technique but not supported by all\r
-    // browsers).\r
-    var iterate = function (el) {\r
-      for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
-        if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
-          var mo = re.exec(i.tagName);\r
-          if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {\r
-            result[result.length] = new TocEntry(i, getText(i), mo[1]-1);\r
-          }\r
-          iterate(i);\r
-        }\r
-      }\r
-    }\r
-    iterate(el);\r
-    return result;\r
-  }\r
-\r
-  var toc = document.getElementById("toc");\r
-  if (!toc) {\r
-    return;\r
-  }\r
-\r
-  // Delete existing TOC entries in case we're reloading the TOC.\r
-  var tocEntriesToRemove = [];\r
-  var i;\r
-  for (i = 0; i < toc.childNodes.length; i++) {\r
-    var entry = toc.childNodes[i];\r
-    if (entry.nodeName.toLowerCase() == 'div'\r
-     && entry.getAttribute("class")\r
-     && entry.getAttribute("class").match(/^toclevel/))\r
-      tocEntriesToRemove.push(entry);\r
-  }\r
-  for (i = 0; i < tocEntriesToRemove.length; i++) {\r
-    toc.removeChild(tocEntriesToRemove[i]);\r
-  }\r
-\r
-  // Rebuild TOC entries.\r
-  var entries = tocEntries(document.getElementById("content"), toclevels);\r
-  for (var i = 0; i < entries.length; ++i) {\r
-    var entry = entries[i];\r
-    if (entry.element.id == "")\r
-      entry.element.id = "_toc_" + i;\r
-    var a = document.createElement("a");\r
-    a.href = "#" + entry.element.id;\r
-    a.appendChild(document.createTextNode(entry.text));\r
-    var div = document.createElement("div");\r
-    div.appendChild(a);\r
-    div.className = "toclevel" + entry.toclevel;\r
-    toc.appendChild(div);\r
-  }\r
-  if (entries.length == 0)\r
-    toc.parentNode.removeChild(toc);\r
-},\r
-\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Footnotes generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Based on footnote generation code from:\r
- * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html\r
- */\r
-\r
-footnotes: function () {\r
-  // Delete existing footnote entries in case we're reloading the footnodes.\r
-  var i;\r
-  var noteholder = document.getElementById("footnotes");\r
-  if (!noteholder) {\r
-    return;\r
-  }\r
-  var entriesToRemove = [];\r
-  for (i = 0; i < noteholder.childNodes.length; i++) {\r
-    var entry = noteholder.childNodes[i];\r
-    if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")\r
-      entriesToRemove.push(entry);\r
-  }\r
-  for (i = 0; i < entriesToRemove.length; i++) {\r
-    noteholder.removeChild(entriesToRemove[i]);\r
-  }\r
-\r
-  // Rebuild footnote entries.\r
-  var cont = document.getElementById("content");\r
-  var spans = cont.getElementsByTagName("span");\r
-  var refs = {};\r
-  var n = 0;\r
-  for (i=0; i<spans.length; i++) {\r
-    if (spans[i].className == "footnote") {\r
-      n++;\r
-      var note = spans[i].getAttribute("data-note");\r
-      if (!note) {\r
-        // Use [\s\S] in place of . so multi-line matches work.\r
-        // Because JavaScript has no s (dotall) regex flag.\r
-        note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];\r
-        spans[i].innerHTML =\r
-          "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +\r
-          "' title='View footnote' class='footnote'>" + n + "</a>]";\r
-        spans[i].setAttribute("data-note", note);\r
-      }\r
-      noteholder.innerHTML +=\r
-        "<div class='footnote' id='_footnote_" + n + "'>" +\r
-        "<a href='#_footnoteref_" + n + "' title='Return to text'>" +\r
-        n + "</a>. " + note + "</div>";\r
-      var id =spans[i].getAttribute("id");\r
-      if (id != null) refs["#"+id] = n;\r
-    }\r
-  }\r
-  if (n == 0)\r
-    noteholder.parentNode.removeChild(noteholder);\r
-  else {\r
-    // Process footnoterefs.\r
-    for (i=0; i<spans.length; i++) {\r
-      if (spans[i].className == "footnoteref") {\r
-        var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");\r
-        href = href.match(/#.*/)[0];  // Because IE return full URL.\r
-        n = refs[href];\r
-        spans[i].innerHTML =\r
-          "[<a href='#_footnote_" + n +\r
-          "' title='View footnote' class='footnote'>" + n + "</a>]";\r
-      }\r
-    }\r
-  }\r
-},\r
-\r
-install: function(toclevels) {\r
-  var timerId;\r
-\r
-  function reinstall() {\r
-    asciidoc.footnotes();\r
-    if (toclevels) {\r
-      asciidoc.toc(toclevels);\r
-    }\r
-  }\r
-\r
-  function reinstallAndRemoveTimer() {\r
-    clearInterval(timerId);\r
-    reinstall();\r
-  }\r
-\r
-  timerId = setInterval(reinstall, 500);\r
-  if (document.addEventListener)\r
-    document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);\r
-  else\r
-    window.onload = reinstallAndRemoveTimer;\r
-}\r
-\r
-}\r
-asciidoc.install(2);\r
-/*]]>*/\r
-</script>\r
-</head>\r
-<body class="article">\r
-<div id="header">\r
-<h1>ccache installation</h1>\r
-<span id="revnumber">version 3.2.9</span>\r
-<div id="toc">
-  <div id="toctitle">Table of Contents</div>
-  <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
-</div>\r
-</div>\r
-<div id="content">\r
-<div class="sect1">\r
-<h2 id="_building_code_from_a_release_archive">Building code from a release archive</h2>\r
-<div class="sectionbody">\r
-<div class="sect2">\r
-<h3 id="_prerequisites">Prerequisites</h3>\r
-<div class="paragraph"><p>To build ccache, you need:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-A C compiler (for instance GCC)\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>It is also recommended that you have:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-zlib <a href="http://www.zlib.net">http://www.zlib.net</a> (if you don&#8217;t have zlib installed, ccache will\r
-  use a bundled copy)\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_installation">Installation</h3>\r
-<div class="paragraph"><p>To compile and install ccache, run these commands:</p></div>\r
-<div class="literalblock">\r
-<div class="content">\r
-<pre><code>./configure\r
-make\r
-make install</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>You may set the installation directory and other parameters by options to\r
-&#8220;./configure&#8221;. To see them, run &#8220;./configure --help&#8221;.</p></div>\r
-<div class="paragraph"><p>There are two ways to use ccache. You can either prefix your compilation\r
-commands with &#8220;ccache&#8221; or you can create a symbolic link (named as your\r
-compiler) to ccache. The first method is most convenient if you just want to\r
-try out ccache or wish to use it for some specific projects. The second method\r
-is most useful for when you wish to use ccache for all your compilations.</p></div>\r
-<div class="paragraph"><p>To install for usage by the first method just copy ccache to somewhere in your\r
-path.</p></div>\r
-<div class="paragraph"><p>To install for the second method, do something like this:</p></div>\r
-<div class="literalblock">\r
-<div class="content">\r
-<pre><code>cp ccache /usr/local/bin/\r
-ln -s ccache /usr/local/bin/gcc\r
-ln -s ccache /usr/local/bin/g++\r
-ln -s ccache /usr/local/bin/cc\r
-ln -s ccache /usr/local/bin/c++</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>And so forth. This will work as long as &#8220;/usr/local/bin&#8221; comes before the\r
-path to the compiler (which is usually in &#8220;/usr/bin&#8221;). After installing you\r
-may wish to run &#8220;which gcc&#8221; to make sure that the correct link is being used.</p></div>\r
-<div class="admonitionblock">\r
-<table><tr>\r
-<td class="icon">\r
-<div class="title">Note</div>\r
-</td>\r
-<td class="content">Do not use a hard link, use a symbolic link. A hard link will cause\r
-&#8220;interesting&#8221; problems.</td>\r
-</tr></table>\r
-</div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_building_code_from_the_source_code_repository">Building code from the source code repository</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>In addition to the prerequisites mentioned above, you also need:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-AsciiDoc (<a href="http://www.methods.co.nz/asciidoc/">http://www.methods.co.nz/asciidoc/</a>) to build the documentation.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Autoconf (<a href="http://www.gnu.org/software/autoconf/">http://www.gnu.org/software/autoconf/</a>)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-gperf (<a href="http://www.gnu.org/software/gperf/">http://www.gnu.org/software/gperf/</a>)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-xsltproc (<a href="http://xmlsoft.org/XSLT/xsltproc2.html">http://xmlsoft.org/XSLT/xsltproc2.html</a>)\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>To debug and run the performance test suite you&#8217;ll also need:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Python (<a href="http://www.python.org/">http://www.python.org/</a>)\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>Run "./autogen.sh" and then follow the steps mentioned under "Installation"\r
-above.</p></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div id="footnotes"><hr /></div>\r
-<div id="footer">\r
-<div id="footer-text">\r
-Version 3.2.9<br />\r
-Last updated\r
- 2016-09-28 21:28:19 CEST\r
-</div>\r
-</div>\r
-</body>\r
-</html>\r
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644 (file)
index 0000000..be25d02
--- /dev/null
@@ -0,0 +1,51 @@
+ccache installation
+===================
+
+Prerequisites
+-------------
+
+To build ccache, you need:
+
+- A C compiler (for instance GCC)
+
+It is also recommended that you have:
+
+- [zlib](http://www.zlib.net) (if you don't have zlib installed, ccache will
+  use a bundled copy)
+
+
+Installation
+------------
+
+To compile and install ccache, run these commands:
+
+    ./configure
+    make
+    make install
+
+You may set the installation directory and other parameters by options to
+`./configure`. To see them, run `./configure --help`.
+
+There are two ways to use ccache. You can either prefix your compilation
+commands with `ccache` or you can create a symbolic link (named as your
+compiler) to ccache. The first method is most convenient if you just want to
+try out ccache or wish to use it for some specific projects. The second method
+is most useful for when you wish to use ccache for all your compilations.
+
+To install for usage by the first method just copy ccache to somewhere in your
+path.
+
+To install for the second method, do something like this:
+
+    cp ccache /usr/local/bin/
+    ln -s ccache /usr/local/bin/gcc
+    ln -s ccache /usr/local/bin/g++
+    ln -s ccache /usr/local/bin/cc
+    ln -s ccache /usr/local/bin/c++
+
+And so forth. This will work as long as `/usr/local/bin` comes before the path
+to the compiler (which is usually in `/usr/bin`). After installing you may wish
+to run `which gcc` to make sure that the correct link is being used.
+
+NOTE: Do not use a hard link, use a symbolic link. A hard link will cause
+"interesting" problems.
diff --git a/INSTALL.txt b/INSTALL.txt
deleted file mode 100644 (file)
index f77cbe6..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-ccache installation
-===================
-
-
-Building code from a release archive
-------------------------------------
-
-Prerequisites
-~~~~~~~~~~~~~
-
-To build ccache, you need:
-
-- A C compiler (for instance GCC)
-
-It is also recommended that you have:
-
-- zlib <http://www.zlib.net> (if you don't have zlib installed, ccache will
-  use a bundled copy)
-
-
-Installation
-~~~~~~~~~~~~
-
-To compile and install ccache, run these commands:
-
-    ./configure
-    make
-    make install
-
-You may set the installation directory and other parameters by options to
-``./configure''. To see them, run ``./configure --help''.
-
-There are two ways to use ccache. You can either prefix your compilation
-commands with ``ccache'' or you can create a symbolic link (named as your
-compiler) to ccache. The first method is most convenient if you just want to
-try out ccache or wish to use it for some specific projects. The second method
-is most useful for when you wish to use ccache for all your compilations.
-
-To install for usage by the first method just copy ccache to somewhere in your
-path.
-
-To install for the second method, do something like this:
-
-    cp ccache /usr/local/bin/
-    ln -s ccache /usr/local/bin/gcc
-    ln -s ccache /usr/local/bin/g++
-    ln -s ccache /usr/local/bin/cc
-    ln -s ccache /usr/local/bin/c++
-
-And so forth. This will work as long as ``/usr/local/bin'' comes before the
-path to the compiler (which is usually in ``/usr/bin''). After installing you
-may wish to run ``which gcc'' to make sure that the correct link is being used.
-
-NOTE: Do not use a hard link, use a symbolic link. A hard link will cause
-``interesting'' problems.
-
-
-Building code from the source code repository
----------------------------------------------
-
-In addition to the prerequisites mentioned above, you also need:
-
-- AsciiDoc (http://www.methods.co.nz/asciidoc/) to build the documentation.
-- Autoconf (http://www.gnu.org/software/autoconf/)
-- gperf (http://www.gnu.org/software/gperf/)
-- xsltproc (http://xmlsoft.org/XSLT/xsltproc2.html)
-
-To debug and run the performance test suite you'll also need:
-
-- Python (http://www.python.org/)
-
-Run "./autogen.sh" and then follow the steps mentioned under "Installation"
-above.
index 0195c5a..2d441ff 100644 (file)
@@ -734,7 +734,7 @@ asciidoc.install(2);
 <body class="article">\r
 <div id="header">\r
 <h1>ccache copyright and license</h1>\r
-<span id="revnumber">version 3.2.9</span>\r
+<span id="revnumber">version 3.2.7+172_g67acac4</span>\r
 <div id="toc">
   <div id="toctitle">Table of Contents</div>
   <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
@@ -768,7 +768,7 @@ asciidoc.install(2);
 <h2 id="_copyright_and_authors">Copyright and authors</h2>\r
 <div class="sectionbody">\r
 <div class="paragraph"><p>ccache is a collective work with contributions from many people, listed in\r
-AUTHORS.txt and at <a href="http://ccache.samba.org/authors.html">http://ccache.samba.org/authors.html</a>. Subsequent additions\r
+AUTHORS.txt and at <a href="https://ccache.samba.org/authors.html">https://ccache.samba.org/authors.html</a>. Subsequent additions\r
 by contributing authors are implicitly licensed to the public under the same\r
 terms (GNU GPL version 3 or later), but the contributing authors retain\r
 copyrights on their portions of the work.</p></div>\r
@@ -1205,9 +1205,9 @@ following license:</p></div>
 <div id="footnotes"><hr /></div>\r
 <div id="footer">\r
 <div id="footer-text">\r
-Version 3.2.9<br />\r
+Version 3.2.7+172_g67acac4<br />\r
 Last updated\r
- 2016-09-28 21:28:19 CEST\r
+ 2016-07-28 15:37:17 CEST\r
 </div>\r
 </div>\r
 </body>\r
index abe3bd9..a841072 100644 (file)
@@ -29,7 +29,7 @@ Copyright and authors
 ---------------------
 
 ccache is a collective work with contributions from many people, listed in
-AUTHORS.txt and at http://ccache.samba.org/authors.html. Subsequent additions
+AUTHORS.txt and at https://ccache.samba.org/authors.html. Subsequent additions
 by contributing authors are implicitly licensed to the public under the same
 terms (GNU GPL version 3 or later), but the contributing authors retain
 copyrights on their portions of the work.
index 5ba98e9..51812e2 100644 (file)
@@ -734,7 +734,7 @@ asciidoc.install(2);
 <body class="article">\r
 <div id="header">\r
 <h1>CCACHE(1)</h1>\r
-<span id="revnumber">version 3.2.9</span>\r
+<span id="revnumber">version 3.3</span>\r
 <div id="toc">
   <div id="toctitle">Table of Contents</div>
   <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
@@ -767,8 +767,9 @@ again. Supported languages are C, C++, Objective-C and Objective-C++.</p></div>
 <div class="paragraph"><p>ccache has been carefully written to always produce exactly the same compiler\r
 output that you would get without the cache. The only way you should be able to\r
 tell that you are using ccache is the speed. Currently known exceptions to this\r
-goal are listed under <a href="#_bugs">BUGS</a>. If you ever discover an undocumented case\r
-where ccache changes the output of your compiler, please let us know.</p></div>\r
+goal are listed under <a href="#_caveats">CAVEATS</a>. If you ever discover an\r
+undocumented case where ccache changes the output of your compiler, please let\r
+us know.</p></div>\r
 <div class="sect2">\r
 <h3 id="_features">Features</h3>\r
 <div class="ulist"><ul>\r
@@ -1081,6 +1082,9 @@ setting key.</p></div>
     directory. If set to the empty string (which is the default), no rewriting\r
     is done. See also the discussion under\r
     <a href="#_compiling_in_different_directories">COMPILING IN DIFFERENT DIRECTORIES</a>.\r
+    If using GCC or newer versions of Clang, you might want to look into the\r
+    <strong>-fdebug-prefix-map=old=new</strong> option for relocating debug info to a common\r
+    prefix (mapping prefix with old=new).\r
 </p>\r
 </dd>\r
 <dt class="hdlist1">\r
@@ -1266,7 +1270,7 @@ the <strong>prefix_command</strong> setting if possible. See
 <dd>\r
 <p>\r
     This setting is a list of paths to files that ccache will include in the\r
-    the hash sum that idetifies the build. The list separator is semicolon on\r
+    the hash sum that identifies the build. The list separator is semicolon on\r
     Windows systems and colon on other systems.\r
 </p>\r
 </dd>\r
@@ -1290,14 +1294,54 @@ the <strong>prefix_command</strong> setting if possible. See
 </dt>\r
 <dd>\r
 <p>\r
-    If true, ccache will include the current working directory in the hash that\r
-    is used to distinguish two compilations. This prevents a problem with the\r
-    storage of the current working directory in the debug info of an object\r
-    file, which can lead ccache to give a cached object file that has the\r
-    working directory in the debug info set incorrectly. This option is off by\r
-    default as the incorrect setting of this debug info rarely causes problems.\r
-    If you strike problems with GDB not using the correct directory then enable\r
-    this option.\r
+    If true (which is the default), ccache will include the current working\r
+    directory (CWD) in the hash that is used to distinguish two compilations\r
+    when generating debug info (compiler option <strong>-g</strong> with variations).\r
+    Exception: The CWD will not be included in the hash if <strong>base_dir</strong> is set\r
+    (and matches the CWD) and the compiler option <strong>-fdebug-prefix-map</strong> is used.\r
+</p>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>The reason for including the CWD in the hash by default is to prevent a\r
+problem with the storage of the current working directory in the debug info\r
+of an object file, which can lead ccache to return a cached object file\r
+that has the working directory in the debug info set incorrectly.</code></pre>\r
+</div></div>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>You can disable this setting to get cache hits when compiling the same\r
+source code in different directories if you don't mind that CWD in the\r
+debug info might be incorrect.</code></pre>\r
+</div></div>\r
+</dd>\r
+<dt class="hdlist1">\r
+<strong>ignore_headers_in_manifest</strong> (<strong>CCACHE_IGNOREHEADERS</strong>)\r
+</dt>\r
+<dd>\r
+<p>\r
+    This setting is a list of paths to files (or directories with headers) that\r
+    ccache will <strong>not</strong> include in the manifest list that makes up the direct\r
+    mode. Note that this can cause stale cache hits if those headers do indeed\r
+    change. The list separator is semicolon on Windows systems and colon on\r
+    other systems.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<strong>keep_comments_cpp</strong> (<strong>CCACHE_COMMENTS</strong> or <strong>CCACHE_NOCOMMENTS</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
+</dt>\r
+<dd>\r
+<p>\r
+    If true, ccache will not discard the comments before hashing preprocessor\r
+    output. This can be used to check documentation with <strong>-Wdocumentation</strong>.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
+<strong>limit_multiple</strong> (<strong>CCACHE_LIMIT_MULTIPLE</strong>)\r
+</dt>\r
+<dd>\r
+<p>\r
+    Sets the limit when cleaning up. Files are deleted (in LRU order) until the\r
+    levels are below the limit. The default is 0.8 (= 80%).\r
 </p>\r
 </dd>\r
 <dt class="hdlist1">\r
@@ -1351,6 +1395,15 @@ the <strong>prefix_command</strong> setting if possible. See
 </p>\r
 </dd>\r
 <dt class="hdlist1">\r
+<strong>prefix_command_cpp</strong> (<strong>CCACHE_PREFIX_CPP</strong>)\r
+</dt>\r
+<dd>\r
+<p>\r
+    This option adds a list of prefixes (separated by space) to the command\r
+    line that ccache uses when invoking the preprocessor.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
 <strong>read_only</strong> (<strong>CCACHE_READONLY</strong> or <strong>CCACHE_NOREADONLY</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
 </dt>\r
 <dd>\r
@@ -1385,13 +1438,20 @@ the <strong>prefix_command</strong> setting if possible. See
 </dt>\r
 <dd>\r
 <p>\r
-    If true, ccache will not use the optimisation of avoiding the second call\r
-    to the preprocessor by compiling the preprocessed output that was used for\r
-    finding the hash in the case of a cache miss. This is primarily a debugging\r
-    option, although it is possible that some unusual compilers will have\r
-    problems with compiling the preprocessed output, in which case this option\r
-    could allow ccache to be used anyway.\r
+    If true, ccache will first run the preprocessor to preprocess the source\r
+    code (see <a href="#_the_preprocessor_mode">THE PREPROCESSOR MODE</a>) and then on a\r
+    cache miss run the compiler on the source code to get hold of the object\r
+    file. This is the default.\r
 </p>\r
+<div class="literalblock">\r
+<div class="content">\r
+<pre><code>If false, ccache will first run preprocessor to preprocess the source code\r
+and then on a cache miss run the compiler on the _preprocessed source code_\r
+instead of the original source code. This makes cache misses slightly\r
+faster since the source code only has to be preprocessed once. The downside\r
+is that some compilers won't produce the same result (for instance\r
+diagnostics warnings) when compiling preprocessed source code.</code></pre>\r
+</div></div>\r
 </dd>\r
 <dt class="hdlist1">\r
 <strong>sloppiness</strong> (<strong>CCACHE_SLOPPINESS</strong>)\r
@@ -1444,6 +1504,16 @@ the <strong>prefix_command</strong> setting if possible. See
 </p>\r
 </dd>\r
 <dt class="hdlist1">\r
+<strong>no_system_headers</strong>\r
+</dt>\r
+<dd>\r
+<p>\r
+    By default, ccache will also include all system headers in the manifest.\r
+    With this option set, ccache will only include system headers in the hash\r
+    but not add the system header files to the list of include files.\r
+</p>\r
+</dd>\r
+<dt class="hdlist1">\r
 <strong>pch_defines</strong>\r
 </dt>\r
 <dd>\r
@@ -1682,8 +1752,8 @@ a compiler option not supported by the direct mode is used:
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
-a <strong>-Wp,<em>X</em></strong> compiler option other than <strong>-Wp,-MD,<em>path</em></strong> and\r
-   <strong>-Wp,-MMD,<em>path</em></strong>\r
+a <strong>-Wp,<em>X</em></strong> compiler option other than <strong>-Wp,-MD,<em>path</em></strong>,\r
+   <strong>-Wp,-MMD,<em>path</em></strong> and <strong>-Wp,-D_define_</strong>\r
 </p>\r
 </li>\r
 <li>\r
@@ -1940,11 +2010,13 @@ The cached results will not be shared between compilations with and without
 </li>\r
 </ul></div>\r
 <div class="paragraph"><p>Another minor thing is that if <strong>prefix_command</strong> is used, ccache will not invoke\r
-the other wrapper when running the preprocessor, which increase performance.</p></div>\r
+the other wrapper when running the preprocessor, which increases performance.\r
+You can use the <strong>prefix_command_cpp</strong> configuration setting if you also want to\r
+invoke the other wrapper when doing preprocessing (normally by adding <strong>-E</strong>).</p></div>\r
 </div>\r
 </div>\r
 <div class="sect1">\r
-<h2 id="_bugs">Bugs</h2>\r
+<h2 id="_caveats">Caveats</h2>\r
 <div class="sectionbody">\r
 <div class="ulist"><ul>\r
 <li>\r
@@ -1983,7 +2055,7 @@ have to do some adjustments of how you use the compiler and ccache in order to
 improve performance.</p></div>\r
 <div class="paragraph"><p>Since ccache works best when I/O is fast, put the cache directory on a fast\r
 storage device if possible. Having lots of free memory so that files in the\r
-cache directory stay in the disk cache is also preferrable.</p></div>\r
+cache directory stay in the disk cache is also preferable.</p></div>\r
 <div class="paragraph"><p>A good way of monitoring how well ccache works is to run <strong>ccache -s</strong> before and\r
 after your build and then compare the statistics counters. Here are some common\r
 problems and what may be done to increase the hit rate:</p></div>\r
@@ -2010,8 +2082,8 @@ Compiler arguments that are hashed in the direct mode but not in the
 </li>\r
 <li>\r
 <p>\r
-The compiler option <strong>-Xpreprocessor</strong> or <strong>-Wp,<em>X</em></strong> (except <strong>-Wp,-MD,<em>path</em></strong>\r
-   and <strong>Wp,-MMD,<em>path</em></strong>) is used.\r
+The compiler option <strong>-Xpreprocessor</strong> or <strong>-Wp,<em>X</em></strong> (except <strong>-Wp,-MD,<em>path</em></strong>,\r
+   <strong>-Wp,-MMD,<em>path</em></strong>, and <strong>-Wp,-D_define_</strong>) is used.\r
 </p>\r
 </li>\r
 <li>\r
@@ -2109,13 +2181,6 @@ If &#8220;can&#8217;t use precompiled header&#8221; has been incremented, see
 </ul></div>\r
 </div>\r
 <div class="sect2">\r
-<h3 id="_errors_when_compiling_with_ccache">Errors when compiling with ccache</h3>\r
-<div class="paragraph"><p>If compilation doesn&#8217;t work with ccache, but it works without it, one possible\r
-reason is that the compiler can&#8217;t compile preprocessed output correctly. A\r
-workaround that may work is to enable <strong>run_second_cpp</strong>*. This will make cache\r
-misses slower, though, so it is better to find and fix the root cause.</p></div>\r
-</div>\r
-<div class="sect2">\r
 <h3 id="_corrupt_object_files">Corrupt object files</h3>\r
 <div class="paragraph"><p>It should be noted that ccache is susceptible to general storage problems. If a\r
 bad object file sneaks into the cache for some reason, it will of course stay\r
@@ -2152,7 +2217,7 @@ case, please report it.</p></div>
 <h2 id="_more_information">More information</h2>\r
 <div class="sectionbody">\r
 <div class="paragraph"><p>Credits, mailing list information, bug reporting instructions, source code,\r
-etc, can be found on ccache&#8217;s web site: <a href="http://ccache.samba.org">http://ccache.samba.org</a>.</p></div>\r
+etc, can be found on ccache&#8217;s web site: <a href="https://ccache.samba.org">https://ccache.samba.org</a>.</p></div>\r
 </div>\r
 </div>\r
 <div class="sect1">\r
@@ -2160,16 +2225,16 @@ etc, can be found on ccache&#8217;s web site: <a href="http://ccache.samba.org">
 <div class="sectionbody">\r
 <div class="paragraph"><p>ccache was originally written by Andrew Tridgell and is currently developed and\r
 maintained by Joel Rosdahl. See AUTHORS.txt or AUTHORS.html and\r
-<a href="http://ccache.samba.org/credits.html">http://ccache.samba.org/credits.html</a> for a list of contributors.</p></div>\r
+<a href="https://ccache.samba.org/credits.html">https://ccache.samba.org/credits.html</a> for a list of contributors.</p></div>\r
 </div>\r
 </div>\r
 </div>\r
 <div id="footnotes"><hr /></div>\r
 <div id="footer">\r
 <div id="footer-text">\r
-Version 3.2.9<br />\r
+Version 3.3<br />\r
 Last updated\r
- 2016-09-28 21:28:19 CEST\r
+ 2016-08-27 16:48:16 CEST\r
 </div>\r
 </div>\r
 </body>\r
index 5a5edd4..ab01886 100644 (file)
@@ -30,8 +30,9 @@ again. Supported languages are C, C\+\+, Objective-C and Objective-C++.
 ccache has been carefully written to always produce exactly the same compiler
 output that you would get without the cache. The only way you should be able to
 tell that you are using ccache is the speed. Currently known exceptions to this
-goal are listed under <<_bugs,BUGS>>. If you ever discover an undocumented case
-where ccache changes the output of your compiler, please let us know.
+goal are listed under <<_caveats,CAVEATS>>. If you ever discover an
+undocumented case where ccache changes the output of your compiler, please let
+us know.
 
 
 Features
@@ -242,6 +243,9 @@ setting key.
     directory. If set to the empty string (which is the default), no rewriting
     is done. See also the discussion under
     <<_compiling_in_different_directories,COMPILING IN DIFFERENT DIRECTORIES>>.
+    If using GCC or newer versions of Clang, you might want to look into the
+    *-fdebug-prefix-map=old=new* option for relocating debug info to a common
+    prefix (mapping prefix with old=new).
 
 *cache_dir* (*CCACHE_DIR*)::
 
@@ -349,7 +353,7 @@ WRAPPERS>>.
 *extra_files_to_hash* (*CCACHE_EXTRAFILES*)::
 
     This setting is a list of paths to files that ccache will include in the
-    the hash sum that idetifies the build. The list separator is semicolon on
+    the hash sum that identifies the build. The list separator is semicolon on
     Windows systems and colon on other systems.
 
 *hard_link* (*CCACHE_HARDLINK* or *CCACHE_NOHARDLINK*, see <<_boolean_values,Boolean values>> above)::
@@ -365,14 +369,38 @@ WRAPPERS>>.
 
 *hash_dir* (*CCACHE_HASHDIR* or *CCACHE_NOHASHDIR*, see <<_boolean_values,Boolean values>> above)::
 
-    If true, ccache will include the current working directory in the hash that
-    is used to distinguish two compilations. This prevents a problem with the
-    storage of the current working directory in the debug info of an object
-    file, which can lead ccache to give a cached object file that has the
-    working directory in the debug info set incorrectly. This option is off by
-    default as the incorrect setting of this debug info rarely causes problems.
-    If you strike problems with GDB not using the correct directory then enable
-    this option.
+    If true (which is the default), ccache will include the current working
+    directory (CWD) in the hash that is used to distinguish two compilations
+    when generating debug info (compiler option *-g* with variations).
+    Exception: The CWD will not be included in the hash if *base_dir* is set
+    (and matches the CWD) and the compiler option *-fdebug-prefix-map* is used.
+
+    The reason for including the CWD in the hash by default is to prevent a
+    problem with the storage of the current working directory in the debug info
+    of an object file, which can lead ccache to return a cached object file
+    that has the working directory in the debug info set incorrectly.
+
+    You can disable this setting to get cache hits when compiling the same
+    source code in different directories if you don't mind that CWD in the
+    debug info might be incorrect.
+
+*ignore_headers_in_manifest* (*CCACHE_IGNOREHEADERS*)::
+
+    This setting is a list of paths to files (or directories with headers) that
+    ccache will *not* include in the manifest list that makes up the direct
+    mode. Note that this can cause stale cache hits if those headers do indeed
+    change. The list separator is semicolon on Windows systems and colon on
+    other systems.
+
+*keep_comments_cpp* (*CCACHE_COMMENTS* or *CCACHE_NOCOMMENTS*, see <<_boolean_values,Boolean values>> above)::
+
+    If true, ccache will not discard the comments before hashing preprocessor
+    output. This can be used to check documentation with *-Wdocumentation*.
+
+*limit_multiple* (*CCACHE_LIMIT_MULTIPLE*)::
+
+    Sets the limit when cleaning up. Files are deleted (in LRU order) until the
+    levels are below the limit. The default is 0.8 (= 80%).
 
 *log_file* (*CCACHE_LOGFILE*)::
 
@@ -405,6 +433,11 @@ WRAPPERS>>.
     <<_using_ccache_with_other_compiler_wrappers,USING CCACHE WITH OTHER
     COMPILER WRAPPERS>>.
 
+*prefix_command_cpp* (*CCACHE_PREFIX_CPP*)::
+
+    This option adds a list of prefixes (separated by space) to the command
+    line that ccache uses when invoking the preprocessor.
+
 *read_only* (*CCACHE_READONLY* or *CCACHE_NOREADONLY*, see <<_boolean_values,Boolean values>> above)::
 
     If true, ccache will attempt to use existing cached object files, but it
@@ -425,12 +458,17 @@ WRAPPERS>>.
 
 *run_second_cpp* (*CCACHE_CPP2* or *CCACHE_NOCPP2*, see <<_boolean_values,Boolean values>> above)::
 
-    If true, ccache will not use the optimisation of avoiding the second call
-    to the preprocessor by compiling the preprocessed output that was used for
-    finding the hash in the case of a cache miss. This is primarily a debugging
-    option, although it is possible that some unusual compilers will have
-    problems with compiling the preprocessed output, in which case this option
-    could allow ccache to be used anyway.
+    If true, ccache will first run the preprocessor to preprocess the source
+    code (see <<_the_preprocessor_mode,THE PREPROCESSOR MODE>>) and then on a
+    cache miss run the compiler on the source code to get hold of the object
+    file. This is the default.
+
+    If false, ccache will first run preprocessor to preprocess the source code
+    and then on a cache miss run the compiler on the _preprocessed source code_
+    instead of the original source code. This makes cache misses slightly
+    faster since the source code only has to be preprocessed once. The downside
+    is that some compilers won't produce the same result (for instance
+    diagnostics warnings) when compiling preprocessed source code.
 
 *sloppiness* (*CCACHE_SLOPPINESS*)::
 
@@ -453,6 +491,10 @@ WRAPPERS>>.
 *include_file_mtime*::
     By default, ccache will not cache a file if it includes a header whose
     mtime is too new. This option disables that check.
+*no_system_headers*::
+    By default, ccache will also include all system headers in the manifest.
+    With this option set, ccache will only include system headers in the hash
+    but not add the system header files to the list of include files.
 *pch_defines*::
     Be sloppy about #defines when precompiling a header file. See
     <<_precompiled_headers,PRECOMPILED HEADERS>> for more information.
@@ -596,8 +638,8 @@ The direct mode will be disabled if any of the following holds:
   race condition)
 * the unifier is enabled (the configuration setting *unify* is true)
 * a compiler option not supported by the direct mode is used:
-** a *-Wp,_X_* compiler option other than *-Wp,-MD,_path_* and
-   *-Wp,-MMD,_path_*
+** a *-Wp,_X_* compiler option other than *-Wp,-MD,_path_*,
+   *-Wp,-MMD,_path_* and *-Wp,-D_define_*
 ** *-Xpreprocessor*
 * the string ``\_\_TIME__'' is present in the source code
 
@@ -750,11 +792,13 @@ size of the other wrapper instead of the real compiler, which means that:
   the other wrapper.
 
 Another minor thing is that if *prefix_command* is used, ccache will not invoke
-the other wrapper when running the preprocessor, which increase performance.
+the other wrapper when running the preprocessor, which increases performance.
+You can use the *prefix_command_cpp* configuration setting if you also want to
+invoke the other wrapper when doing preprocessing (normally by adding *-E*).
 
 
-Bugs
-----
+Caveats
+-------
 
 * ccache doesn't handle the GNU Assembler's *.incbin* directive correctly. This
   directive can be embedded in the source code inside an *__asm__* statement in
@@ -788,7 +832,7 @@ improve performance.
 
 Since ccache works best when I/O is fast, put the cache directory on a fast
 storage device if possible. Having lots of free memory so that files in the
-cache directory stay in the disk cache is also preferrable.
+cache directory stay in the disk cache is also preferable.
 
 A good way of monitoring how well ccache works is to run *ccache -s* before and
 after your build and then compare the statistics counters. Here are some common
@@ -802,8 +846,8 @@ problems and what may be done to increase the hit rate:
 ** Compiler arguments that are hashed in the direct mode but not in the
    preprocessor mode have changed (*-I*, *-include*, *-D*, etc) and they didn't
    affect the preprocessor output.
-** The compiler option *-Xpreprocessor* or *-Wp,_X_* (except *-Wp,-MD,_path_*
-   and *Wp,-MMD,_path_*) is used.
+** The compiler option *-Xpreprocessor* or *-Wp,_X_* (except *-Wp,-MD,_path_*,
+   *-Wp,-MMD,_path_*, and *-Wp,-D_define_*) is used.
 ** This was the first compilation with a new value of the base directory
    setting.
 ** A modification time of one of the include files is too new (created the same
@@ -857,15 +901,6 @@ problems and what may be done to increase the hit rate:
   <<_precompiled_headers,PRECOMPILED HEADERS>>.
 
 
-Errors when compiling with ccache
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If compilation doesn't work with ccache, but it works without it, one possible
-reason is that the compiler can't compile preprocessed output correctly. A
-workaround that may work is to enable *run_second_cpp**. This will make cache
-misses slower, though, so it is better to find and fix the root cause.
-
-
 Corrupt object files
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -892,7 +927,7 @@ More information
 ----------------
 
 Credits, mailing list information, bug reporting instructions, source code,
-etc, can be found on ccache's web site: <http://ccache.samba.org>.
+etc, can be found on ccache's web site: <https://ccache.samba.org>.
 
 
 Author
@@ -900,4 +935,4 @@ Author
 
 ccache was originally written by Andrew Tridgell and is currently developed and
 maintained by Joel Rosdahl. See AUTHORS.txt or AUTHORS.html and
-<http://ccache.samba.org/credits.html> for a list of contributors.
+<https://ccache.samba.org/credits.html> for a list of contributors.
index d474f2a..5aee02d 100644 (file)
@@ -66,7 +66,7 @@ test_objs = $(test_sources:.c=.o)
 all_sources = $(ccache_sources) $(test_sources)
 all_objs = $(ccache_objs) $(test_objs) $(zlib_objs)
 
-files_to_clean = $(all_objs) ccache$(EXEEXT) test/main$(EXEEXT) *~
+files_to_clean = $(all_objs) ccache$(EXEEXT) test/main$(EXEEXT) *~ testdir.*
 files_to_distclean = Makefile config.h config.log config.status
 
 .PHONY: all
@@ -84,7 +84,7 @@ install: all $(srcdir)/ccache.1
 
 .PHONY: clean
 clean:
-       rm -f $(files_to_clean)
+       rm -rf $(files_to_clean)
 
 conf.c: confitems_lookup.c envtoconfitems_lookup.c
 
index 5466070..39b6ba0 100644 (file)
--- a/NEWS.html
+++ b/NEWS.html
@@ -734,41 +734,151 @@ asciidoc.install(2);
 <body class="article">\r
 <div id="header">\r
 <h1>ccache news</h1>\r
-<span id="revnumber">version 3.2.9</span>\r
+<span id="revnumber">version 3.3</span>\r
 <div id="toc">
   <div id="toctitle">Table of Contents</div>
   <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
 </div>\r
 </div>\r
 <div id="content">\r
-<div id="preamble">\r
+<div class="sect1">\r
+<h2 id="_ccache_3_3">ccache 3.3</h2>\r
 <div class="sectionbody">\r
-<div class="paragraph"><p>ccache 3.2.9</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code>Release date: 2016-09-28\r
-\r
-Bug fixes\r
-~~~~~~~~~\r
-\r
-- Fixed a regression in ccache 3.2.8: ccache could get confused when using the\r
-  compiler option `-Wp,` to pass multiple options to the preprocessor,\r
-  resulting in missing dependency files from direct mode cache hits.\r
-\r
-\r
-ccache 3.2.8</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>Release date: 2016-09-07</p></div>\r
-</div>\r
+<div class="paragraph"><p>Release date: 2016-08-27</p></div>\r
+<div class="sect2">\r
+<h3 id="_notes">Notes</h3>\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+A C99-compatible compiler is now required to build ccache.\r
+</p>\r
+</li>\r
+</ul></div>\r
 </div>\r
 <div class="sect2">\r
-<h3 id="_bug_fixes">Bug fixes</h3>\r
+<h3 id="_new_features_and_improvements">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
-Fixed an issue when compiler option <code>-Wp,-MT,path</code> is used instead of <code>-MT\r
-  path</code> (and similar for <code>-MF</code>, <code>-MP</code> and <code>-MQ</code>) and <code>run_second_cpp</code>\r
-  (<code>CCACHE_CPP2</code>) is enabled.\r
+The configuration option <code>run_second_cpp</code> (<code>CCACHE_CPP2</code>) now defaults to\r
+  true. This improves ccache&#8217;s out-of-the-box experience for compilers that\r
+  can&#8217;t compile their own preprocessed output with the same outcome as if they\r
+  compiled the real source code directly, e.g. newer versions of GCC and Clang.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+The configuration option <code>hash_dir</code> (<code>CCACHE_HASHDIR</code>) now defaults to true.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added a new <code>ignore_headers_in_manifest</code> configuration option, which\r
+  specifies headers that should be ignored in the direct mode.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added a new <code>prefix_command_cpp</code> (<code>CCACHE_PREFIX_CPP</code>) configuration option,\r
+  which specifies one or several prefixes to add to the command line ccache\r
+  uses when invoking the preprocessor.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added a new <code>limit_multiple</code> (<code>CCACHE_LIMIT_MULTIPLE</code>) configuration option,\r
+  which specifies how much of the cache to remove when cleaning.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added a new <code>keep_comments_cpp</code> (<code>CCACHE_COMMENTS</code>) configuration option,\r
+  which tells ccache not to discard the comments before hashing preprocessor\r
+  output. This can be used to check documentation with <strong>-Wdocumentation</strong>.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added a new sloppiness option <code>no_system_headers</code>, which tells ccache not to\r
+  include system headers in manifest files.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added a new statistics counter that tracks the number of performed cleanups\r
+  due to the cache size being over the limit. The value is shown in the output\r
+  of &#8220;ccache -s&#8221;.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added support for relocating debug info directory using <code>-fdebug-prefix-map</code>.\r
+  This allows for cache hits even when <code>hash_dir</code> is used in combination with\r
+  <code>base_dir</code>.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added a new &#8220;cache hit rate&#8221; field to the output of &#8220;ccache -s&#8221;.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added support for caching compilation of assembler code produced by e.g.\r
+  &#8220;gcc -S file.c&#8221;.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added support for cuda including the -optf/--options-file option.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added support for Fortran 77.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Added support for multiple <code>-arch</code> options to produce "fat binaries".\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Multiple identical <code>-arch</code> arguments are now handled without bailing.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+The concatenated form of some long compiler options is now recognized, for\r
+  example when using <code>-isystemPATH</code> instead of <code>-isystem PATH</code>.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+If hard-linking is enabled and but fails (e.g. due to cross-device linking),\r
+  ccache now falls back to copying instead of running the compiler.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Made the <code>hash_dir</code> option only have effect when generating debug info.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+ccache now knows how to convert absolute paths to relative paths inside\r
+  dependency files when using <code>base_dir</code>.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Improved parsing of <code>-g*</code> options.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Made ccache understand <code>-Wp,-D*</code> options.\r
 </p>\r
 </li>\r
 <li>\r
@@ -777,8 +887,43 @@ ccache now understands the undocumented <code>-coverage</code> (only one dash) G
   option.\r
 </p>\r
 </li>\r
+<li>\r
+<p>\r
+Names of included files are no longer included in the hash of the compiler&#8217;s\r
+  preprocessed output. This leads to more potential cache hits when not using\r
+  the direct mode.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Increased buffer size used when reading file data. This improves performance\r
+  slightly.\r
+</p>\r
+</li>\r
+</ul></div>\r
+</div>\r
+<div class="sect2">\r
+<h3 id="_bug_fixes">Bug fixes</h3>\r
+<div class="ulist"><ul>\r
+<li>\r
+<p>\r
+Bail out on too hard compiler option <code>-P</code>.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Fixed clang test suite when running on Linux.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Fixed build and test for MinGW32 and Windows.\r
+</p>\r
+</li>\r
 </ul></div>\r
 </div>\r
+</div>\r
+</div>\r
 <div class="sect1">\r
 <h2 id="_ccache_3_2_7">ccache 3.2.7</h2>\r
 <div class="sectionbody">\r
@@ -836,7 +981,7 @@ Fixed a bug where (due to ccache rewriting paths) the compiler could choose
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2016-04-17</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_2">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -962,7 +1107,7 @@ Fixed a bug where cache cleanup could be run too early for caches larger than
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2015-08-16</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_2">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_3">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -1009,7 +1154,7 @@ Only log "Disabling direct mode" once when failing to read potential include
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2015-05-10</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_3">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_4">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -1138,7 +1283,7 @@ Added missing documentation for <code>max_files</code> and <code>max_size</code>
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2014-11-17</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_4">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_5">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -1409,7 +1554,7 @@ Don&#8217;t try to reset a non-existing stats file. This avoids &#8220;No such f
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2014-10-19</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_5">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_6">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -1551,7 +1696,7 @@ Fixed test suite to work on ecryptfs.
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2012-08-11</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_6">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_7">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -1693,7 +1838,7 @@ Improved documentation on how to fix bad object files in the cache.
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2011-08-21</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_7">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_8">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -1724,7 +1869,7 @@ Fixed alignment of &#8220;called for preprocessing&#8221; counter.
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2011-05-29</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_8">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_9">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -1951,7 +2096,7 @@ Minor debug log message improvements.
 <div class="sectionbody">\r
 <div class="paragraph"><p>Release date: 2010-09-16</p></div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_9">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_10">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -2151,7 +2296,7 @@ The way the hashes are calculated has changed, so you won&#8217;t get cache hits
 </ul></div>\r
 </div>\r
 <div class="sect2">\r
-<h3 id="_new_features_and_improvements_10">New features and improvements</h3>\r
+<h3 id="_new_features_and_improvements_11">New features and improvements</h3>\r
 <div class="ulist"><ul>\r
 <li>\r
 <p>\r
@@ -2439,9 +2584,9 @@ Statistics counters are now correctly updated for -E option failures and
 <div id="footnotes"><hr /></div>\r
 <div id="footer">\r
 <div id="footer-text">\r
-Version 3.2.9<br />\r
+Version 3.3<br />\r
 Last updated\r
- 2016-09-28 22:19:01 CEST\r
+ 2016-08-27 16:52:44 CEST\r
 </div>\r
 </div>\r
 </body>\r
index 2cd2b32..c3434a3 100644 (file)
--- a/NEWS.txt
+++ b/NEWS.txt
 ccache news
 ===========
 
-ccache 3.2.9
-----------------
-Release date: 2016-09-28
 
-Bug fixes
-~~~~~~~~~
+ccache 3.3
+----------
+Release date: 2016-08-27
 
-- Fixed a regression in ccache 3.2.8: ccache could get confused when using the
-  compiler option `-Wp,` to pass multiple options to the preprocessor,
-  resulting in missing dependency files from direct mode cache hits.
+Notes
+~~~~~
 
+- A C99-compatible compiler is now required to build ccache.
 
-ccache 3.2.8
-------------
-Release date: 2016-09-07
 
-Bug fixes
-~~~~~~~~~
+New features and improvements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- The configuration option `run_second_cpp` (`CCACHE_CPP2`) now defaults to
+  true. This improves ccache's out-of-the-box experience for compilers that
+  can't compile their own preprocessed output with the same outcome as if they
+  compiled the real source code directly, e.g. newer versions of GCC and Clang.
+
+- The configuration option `hash_dir` (`CCACHE_HASHDIR`) now defaults to true.
+
+- Added a new `ignore_headers_in_manifest` configuration option, which
+  specifies headers that should be ignored in the direct mode.
+
+- Added a new `prefix_command_cpp` (`CCACHE_PREFIX_CPP`) configuration option,
+  which specifies one or several prefixes to add to the command line ccache
+  uses when invoking the preprocessor.
+
+- Added a new `limit_multiple` (`CCACHE_LIMIT_MULTIPLE`) configuration option,
+  which specifies how much of the cache to remove when cleaning.
+
+- Added a new `keep_comments_cpp` (`CCACHE_COMMENTS`) configuration option,
+  which tells ccache not to discard the comments before hashing preprocessor
+  output. This can be used to check documentation with *-Wdocumentation*.
+
+- Added a new sloppiness option `no_system_headers`, which tells ccache not to
+  include system headers in manifest files.
+
+- Added a new statistics counter that tracks the number of performed cleanups
+  due to the cache size being over the limit. The value is shown in the output
+  of ``ccache -s''.
+
+- Added support for relocating debug info directory using `-fdebug-prefix-map`.
+  This allows for cache hits even when `hash_dir` is used in combination with
+  `base_dir`.
+
+- Added a new ``cache hit rate'' field to the output of ``ccache -s''.
+
+- Added support for caching compilation of assembler code produced by e.g.
+  ``gcc -S file.c''.
 
-- Fixed an issue when compiler option `-Wp,-MT,path` is used instead of `-MT
-  path` (and similar for `-MF`, `-MP` and `-MQ`) and `run_second_cpp`
-  (`CCACHE_CPP2`) is enabled.
+- Added support for cuda including the -optf/--options-file option.
+
+- Added support for Fortran 77.
+
+- Added support for multiple `-arch` options to produce "fat binaries".
+
+- Multiple identical `-arch` arguments are now handled without bailing.
+
+- The concatenated form of some long compiler options is now recognized, for
+  example when using `-isystemPATH` instead of `-isystem PATH`.
+
+- If hard-linking is enabled and but fails (e.g. due to cross-device linking),
+  ccache now falls back to copying instead of running the compiler.
+
+- Made the `hash_dir` option only have effect when generating debug info.
+
+- ccache now knows how to convert absolute paths to relative paths inside
+  dependency files when using `base_dir`.
+
+- Improved parsing of `-g*` options.
+
+- Made ccache understand `-Wp,-D*` options.
 
 - ccache now understands the undocumented `-coverage` (only one dash) GCC
   option.
 
+- Names of included files are no longer included in the hash of the compiler's
+  preprocessed output. This leads to more potential cache hits when not using
+  the direct mode.
+
+- Increased buffer size used when reading file data. This improves performance
+  slightly.
+
+
+Bug fixes
+~~~~~~~~~
+
+- Bail out on too hard compiler option `-P`.
+
+- Fixed clang test suite when running on Linux.
+
+- Fixed build and test for MinGW32 and Windows.
+
 
 ccache 3.2.7
 ------------
@@ -63,6 +131,7 @@ ccache 3.2.5
 ------------
 Release date: 2016-04-17
 
+
 New features and improvements
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/README.html b/README.html
deleted file mode 100644 (file)
index 9bff75d..0000000
+++ /dev/null
@@ -1,839 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
-    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<head>\r
-<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
-<meta name="generator" content="AsciiDoc 8.6.9" />\r
-<title>ccache README</title>\r
-<style type="text/css">\r
-/* Shared CSS for AsciiDoc xhtml11 and html5 backends */\r
-\r
-/* Default font. */\r
-body {\r
-  font-family: Georgia,serif;\r
-}\r
-\r
-/* Title font. */\r
-h1, h2, h3, h4, h5, h6,\r
-div.title, caption.title,\r
-thead, p.table.header,\r
-#toctitle,\r
-#author, #revnumber, #revdate, #revremark,\r
-#footer {\r
-  font-family: Arial,Helvetica,sans-serif;\r
-}\r
-\r
-body {\r
-  margin: 1em 5% 1em 5%;\r
-}\r
-\r
-a {\r
-  color: blue;\r
-  text-decoration: underline;\r
-}\r
-a:visited {\r
-  color: fuchsia;\r
-}\r
-\r
-em {\r
-  font-style: italic;\r
-  color: navy;\r
-}\r
-\r
-strong {\r
-  font-weight: bold;\r
-  color: #083194;\r
-}\r
-\r
-h1, h2, h3, h4, h5, h6 {\r
-  color: #527bbd;\r
-  margin-top: 1.2em;\r
-  margin-bottom: 0.5em;\r
-  line-height: 1.3;\r
-}\r
-\r
-h1, h2, h3 {\r
-  border-bottom: 2px solid silver;\r
-}\r
-h2 {\r
-  padding-top: 0.5em;\r
-}\r
-h3 {\r
-  float: left;\r
-}\r
-h3 + * {\r
-  clear: left;\r
-}\r
-h5 {\r
-  font-size: 1.0em;\r
-}\r
-\r
-div.sectionbody {\r
-  margin-left: 0;\r
-}\r
-\r
-hr {\r
-  border: 1px solid silver;\r
-}\r
-\r
-p {\r
-  margin-top: 0.5em;\r
-  margin-bottom: 0.5em;\r
-}\r
-\r
-ul, ol, li > p {\r
-  margin-top: 0;\r
-}\r
-ul > li     { color: #aaa; }\r
-ul > li > * { color: black; }\r
-\r
-.monospaced, code, pre {\r
-  font-family: "Courier New", Courier, monospace;\r
-  font-size: inherit;\r
-  color: navy;\r
-  padding: 0;\r
-  margin: 0;\r
-}\r
-pre {\r
-  white-space: pre-wrap;\r
-}\r
-\r
-#author {\r
-  color: #527bbd;\r
-  font-weight: bold;\r
-  font-size: 1.1em;\r
-}\r
-#email {\r
-}\r
-#revnumber, #revdate, #revremark {\r
-}\r
-\r
-#footer {\r
-  font-size: small;\r
-  border-top: 2px solid silver;\r
-  padding-top: 0.5em;\r
-  margin-top: 4.0em;\r
-}\r
-#footer-text {\r
-  float: left;\r
-  padding-bottom: 0.5em;\r
-}\r
-#footer-badges {\r
-  float: right;\r
-  padding-bottom: 0.5em;\r
-}\r
-\r
-#preamble {\r
-  margin-top: 1.5em;\r
-  margin-bottom: 1.5em;\r
-}\r
-div.imageblock, div.exampleblock, div.verseblock,\r
-div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
-div.admonitionblock {\r
-  margin-top: 1.0em;\r
-  margin-bottom: 1.5em;\r
-}\r
-div.admonitionblock {\r
-  margin-top: 2.0em;\r
-  margin-bottom: 2.0em;\r
-  margin-right: 10%;\r
-  color: #606060;\r
-}\r
-\r
-div.content { /* Block element content. */\r
-  padding: 0;\r
-}\r
-\r
-/* Block element titles. */\r
-div.title, caption.title {\r
-  color: #527bbd;\r
-  font-weight: bold;\r
-  text-align: left;\r
-  margin-top: 1.0em;\r
-  margin-bottom: 0.5em;\r
-}\r
-div.title + * {\r
-  margin-top: 0;\r
-}\r
-\r
-td div.title:first-child {\r
-  margin-top: 0.0em;\r
-}\r
-div.content div.title:first-child {\r
-  margin-top: 0.0em;\r
-}\r
-div.content + div.title {\r
-  margin-top: 0.0em;\r
-}\r
-\r
-div.sidebarblock > div.content {\r
-  background: #ffffee;\r
-  border: 1px solid #dddddd;\r
-  border-left: 4px solid #f0f0f0;\r
-  padding: 0.5em;\r
-}\r
-\r
-div.listingblock > div.content {\r
-  border: 1px solid #dddddd;\r
-  border-left: 5px solid #f0f0f0;\r
-  background: #f8f8f8;\r
-  padding: 0.5em;\r
-}\r
-\r
-div.quoteblock, div.verseblock {\r
-  padding-left: 1.0em;\r
-  margin-left: 1.0em;\r
-  margin-right: 10%;\r
-  border-left: 5px solid #f0f0f0;\r
-  color: #888;\r
-}\r
-\r
-div.quoteblock > div.attribution {\r
-  padding-top: 0.5em;\r
-  text-align: right;\r
-}\r
-\r
-div.verseblock > pre.content {\r
-  font-family: inherit;\r
-  font-size: inherit;\r
-}\r
-div.verseblock > div.attribution {\r
-  padding-top: 0.75em;\r
-  text-align: left;\r
-}\r
-/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
-div.verseblock + div.attribution {\r
-  text-align: left;\r
-}\r
-\r
-div.admonitionblock .icon {\r
-  vertical-align: top;\r
-  font-size: 1.1em;\r
-  font-weight: bold;\r
-  text-decoration: underline;\r
-  color: #527bbd;\r
-  padding-right: 0.5em;\r
-}\r
-div.admonitionblock td.content {\r
-  padding-left: 0.5em;\r
-  border-left: 3px solid #dddddd;\r
-}\r
-\r
-div.exampleblock > div.content {\r
-  border-left: 3px solid #dddddd;\r
-  padding-left: 0.5em;\r
-}\r
-\r
-div.imageblock div.content { padding-left: 0; }\r
-span.image img { border-style: none; vertical-align: text-bottom; }\r
-a.image:visited { color: white; }\r
-\r
-dl {\r
-  margin-top: 0.8em;\r
-  margin-bottom: 0.8em;\r
-}\r
-dt {\r
-  margin-top: 0.5em;\r
-  margin-bottom: 0;\r
-  font-style: normal;\r
-  color: navy;\r
-}\r
-dd > *:first-child {\r
-  margin-top: 0.1em;\r
-}\r
-\r
-ul, ol {\r
-    list-style-position: outside;\r
-}\r
-ol.arabic {\r
-  list-style-type: decimal;\r
-}\r
-ol.loweralpha {\r
-  list-style-type: lower-alpha;\r
-}\r
-ol.upperalpha {\r
-  list-style-type: upper-alpha;\r
-}\r
-ol.lowerroman {\r
-  list-style-type: lower-roman;\r
-}\r
-ol.upperroman {\r
-  list-style-type: upper-roman;\r
-}\r
-\r
-div.compact ul, div.compact ol,\r
-div.compact p, div.compact p,\r
-div.compact div, div.compact div {\r
-  margin-top: 0.1em;\r
-  margin-bottom: 0.1em;\r
-}\r
-\r
-tfoot {\r
-  font-weight: bold;\r
-}\r
-td > div.verse {\r
-  white-space: pre;\r
-}\r
-\r
-div.hdlist {\r
-  margin-top: 0.8em;\r
-  margin-bottom: 0.8em;\r
-}\r
-div.hdlist tr {\r
-  padding-bottom: 15px;\r
-}\r
-dt.hdlist1.strong, td.hdlist1.strong {\r
-  font-weight: bold;\r
-}\r
-td.hdlist1 {\r
-  vertical-align: top;\r
-  font-style: normal;\r
-  padding-right: 0.8em;\r
-  color: navy;\r
-}\r
-td.hdlist2 {\r
-  vertical-align: top;\r
-}\r
-div.hdlist.compact tr {\r
-  margin: 0;\r
-  padding-bottom: 0;\r
-}\r
-\r
-.comment {\r
-  background: yellow;\r
-}\r
-\r
-.footnote, .footnoteref {\r
-  font-size: 0.8em;\r
-}\r
-\r
-span.footnote, span.footnoteref {\r
-  vertical-align: super;\r
-}\r
-\r
-#footnotes {\r
-  margin: 20px 0 20px 0;\r
-  padding: 7px 0 0 0;\r
-}\r
-\r
-#footnotes div.footnote {\r
-  margin: 0 0 5px 0;\r
-}\r
-\r
-#footnotes hr {\r
-  border: none;\r
-  border-top: 1px solid silver;\r
-  height: 1px;\r
-  text-align: left;\r
-  margin-left: 0;\r
-  width: 20%;\r
-  min-width: 100px;\r
-}\r
-\r
-div.colist td {\r
-  padding-right: 0.5em;\r
-  padding-bottom: 0.3em;\r
-  vertical-align: top;\r
-}\r
-div.colist td img {\r
-  margin-top: 0.3em;\r
-}\r
-\r
-@media print {\r
-  #footer-badges { display: none; }\r
-}\r
-\r
-#toc {\r
-  margin-bottom: 2.5em;\r
-}\r
-\r
-#toctitle {\r
-  color: #527bbd;\r
-  font-size: 1.1em;\r
-  font-weight: bold;\r
-  margin-top: 1.0em;\r
-  margin-bottom: 0.1em;\r
-}\r
-\r
-div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
-  margin-top: 0;\r
-  margin-bottom: 0;\r
-}\r
-div.toclevel2 {\r
-  margin-left: 2em;\r
-  font-size: 0.9em;\r
-}\r
-div.toclevel3 {\r
-  margin-left: 4em;\r
-  font-size: 0.9em;\r
-}\r
-div.toclevel4 {\r
-  margin-left: 6em;\r
-  font-size: 0.9em;\r
-}\r
-\r
-span.aqua { color: aqua; }\r
-span.black { color: black; }\r
-span.blue { color: blue; }\r
-span.fuchsia { color: fuchsia; }\r
-span.gray { color: gray; }\r
-span.green { color: green; }\r
-span.lime { color: lime; }\r
-span.maroon { color: maroon; }\r
-span.navy { color: navy; }\r
-span.olive { color: olive; }\r
-span.purple { color: purple; }\r
-span.red { color: red; }\r
-span.silver { color: silver; }\r
-span.teal { color: teal; }\r
-span.white { color: white; }\r
-span.yellow { color: yellow; }\r
-\r
-span.aqua-background { background: aqua; }\r
-span.black-background { background: black; }\r
-span.blue-background { background: blue; }\r
-span.fuchsia-background { background: fuchsia; }\r
-span.gray-background { background: gray; }\r
-span.green-background { background: green; }\r
-span.lime-background { background: lime; }\r
-span.maroon-background { background: maroon; }\r
-span.navy-background { background: navy; }\r
-span.olive-background { background: olive; }\r
-span.purple-background { background: purple; }\r
-span.red-background { background: red; }\r
-span.silver-background { background: silver; }\r
-span.teal-background { background: teal; }\r
-span.white-background { background: white; }\r
-span.yellow-background { background: yellow; }\r
-\r
-span.big { font-size: 2em; }\r
-span.small { font-size: 0.6em; }\r
-\r
-span.underline { text-decoration: underline; }\r
-span.overline { text-decoration: overline; }\r
-span.line-through { text-decoration: line-through; }\r
-\r
-div.unbreakable { page-break-inside: avoid; }\r
-\r
-\r
-/*\r
- * xhtml11 specific\r
- *\r
- * */\r
-\r
-div.tableblock {\r
-  margin-top: 1.0em;\r
-  margin-bottom: 1.5em;\r
-}\r
-div.tableblock > table {\r
-  border: 3px solid #527bbd;\r
-}\r
-thead, p.table.header {\r
-  font-weight: bold;\r
-  color: #527bbd;\r
-}\r
-p.table {\r
-  margin-top: 0;\r
-}\r
-/* Because the table frame attribute is overriden by CSS in most browsers. */\r
-div.tableblock > table[frame="void"] {\r
-  border-style: none;\r
-}\r
-div.tableblock > table[frame="hsides"] {\r
-  border-left-style: none;\r
-  border-right-style: none;\r
-}\r
-div.tableblock > table[frame="vsides"] {\r
-  border-top-style: none;\r
-  border-bottom-style: none;\r
-}\r
-\r
-\r
-/*\r
- * html5 specific\r
- *\r
- * */\r
-\r
-table.tableblock {\r
-  margin-top: 1.0em;\r
-  margin-bottom: 1.5em;\r
-}\r
-thead, p.tableblock.header {\r
-  font-weight: bold;\r
-  color: #527bbd;\r
-}\r
-p.tableblock {\r
-  margin-top: 0;\r
-}\r
-table.tableblock {\r
-  border-width: 3px;\r
-  border-spacing: 0px;\r
-  border-style: solid;\r
-  border-color: #527bbd;\r
-  border-collapse: collapse;\r
-}\r
-th.tableblock, td.tableblock {\r
-  border-width: 1px;\r
-  padding: 4px;\r
-  border-style: solid;\r
-  border-color: #527bbd;\r
-}\r
-\r
-table.tableblock.frame-topbot {\r
-  border-left-style: hidden;\r
-  border-right-style: hidden;\r
-}\r
-table.tableblock.frame-sides {\r
-  border-top-style: hidden;\r
-  border-bottom-style: hidden;\r
-}\r
-table.tableblock.frame-none {\r
-  border-style: hidden;\r
-}\r
-\r
-th.tableblock.halign-left, td.tableblock.halign-left {\r
-  text-align: left;\r
-}\r
-th.tableblock.halign-center, td.tableblock.halign-center {\r
-  text-align: center;\r
-}\r
-th.tableblock.halign-right, td.tableblock.halign-right {\r
-  text-align: right;\r
-}\r
-\r
-th.tableblock.valign-top, td.tableblock.valign-top {\r
-  vertical-align: top;\r
-}\r
-th.tableblock.valign-middle, td.tableblock.valign-middle {\r
-  vertical-align: middle;\r
-}\r
-th.tableblock.valign-bottom, td.tableblock.valign-bottom {\r
-  vertical-align: bottom;\r
-}\r
-\r
-\r
-/*\r
- * manpage specific\r
- *\r
- * */\r
-\r
-body.manpage h1 {\r
-  padding-top: 0.5em;\r
-  padding-bottom: 0.5em;\r
-  border-top: 2px solid silver;\r
-  border-bottom: 2px solid silver;\r
-}\r
-body.manpage h2 {\r
-  border-style: none;\r
-}\r
-body.manpage div.sectionbody {\r
-  margin-left: 3em;\r
-}\r
-\r
-@media print {\r
-  body.manpage div#toc { display: none; }\r
-}\r
-\r
-\r
-</style>\r
-<script type="text/javascript">\r
-/*<![CDATA[*/\r
-var asciidoc = {  // Namespace.\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Table Of Contents generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Author: Mihai Bazon, September 2002\r
- * http://students.infoiasi.ro/~mishoo\r
- *\r
- * Table Of Content generator\r
- * Version: 0.4\r
- *\r
- * Feel free to use this script under the terms of the GNU General Public\r
- * License, as long as you do not remove or alter this notice.\r
- */\r
-\r
- /* modified by Troy D. Hanson, September 2006. License: GPL */\r
- /* modified by Stuart Rackham, 2006, 2009. License: GPL */\r
-\r
-// toclevels = 1..4.\r
-toc: function (toclevels) {\r
-\r
-  function getText(el) {\r
-    var text = "";\r
-    for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
-      if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.\r
-        text += i.data;\r
-      else if (i.firstChild != null)\r
-        text += getText(i);\r
-    }\r
-    return text;\r
-  }\r
-\r
-  function TocEntry(el, text, toclevel) {\r
-    this.element = el;\r
-    this.text = text;\r
-    this.toclevel = toclevel;\r
-  }\r
-\r
-  function tocEntries(el, toclevels) {\r
-    var result = new Array;\r
-    var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');\r
-    // Function that scans the DOM tree for header elements (the DOM2\r
-    // nodeIterator API would be a better technique but not supported by all\r
-    // browsers).\r
-    var iterate = function (el) {\r
-      for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
-        if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
-          var mo = re.exec(i.tagName);\r
-          if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {\r
-            result[result.length] = new TocEntry(i, getText(i), mo[1]-1);\r
-          }\r
-          iterate(i);\r
-        }\r
-      }\r
-    }\r
-    iterate(el);\r
-    return result;\r
-  }\r
-\r
-  var toc = document.getElementById("toc");\r
-  if (!toc) {\r
-    return;\r
-  }\r
-\r
-  // Delete existing TOC entries in case we're reloading the TOC.\r
-  var tocEntriesToRemove = [];\r
-  var i;\r
-  for (i = 0; i < toc.childNodes.length; i++) {\r
-    var entry = toc.childNodes[i];\r
-    if (entry.nodeName.toLowerCase() == 'div'\r
-     && entry.getAttribute("class")\r
-     && entry.getAttribute("class").match(/^toclevel/))\r
-      tocEntriesToRemove.push(entry);\r
-  }\r
-  for (i = 0; i < tocEntriesToRemove.length; i++) {\r
-    toc.removeChild(tocEntriesToRemove[i]);\r
-  }\r
-\r
-  // Rebuild TOC entries.\r
-  var entries = tocEntries(document.getElementById("content"), toclevels);\r
-  for (var i = 0; i < entries.length; ++i) {\r
-    var entry = entries[i];\r
-    if (entry.element.id == "")\r
-      entry.element.id = "_toc_" + i;\r
-    var a = document.createElement("a");\r
-    a.href = "#" + entry.element.id;\r
-    a.appendChild(document.createTextNode(entry.text));\r
-    var div = document.createElement("div");\r
-    div.appendChild(a);\r
-    div.className = "toclevel" + entry.toclevel;\r
-    toc.appendChild(div);\r
-  }\r
-  if (entries.length == 0)\r
-    toc.parentNode.removeChild(toc);\r
-},\r
-\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Footnotes generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Based on footnote generation code from:\r
- * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html\r
- */\r
-\r
-footnotes: function () {\r
-  // Delete existing footnote entries in case we're reloading the footnodes.\r
-  var i;\r
-  var noteholder = document.getElementById("footnotes");\r
-  if (!noteholder) {\r
-    return;\r
-  }\r
-  var entriesToRemove = [];\r
-  for (i = 0; i < noteholder.childNodes.length; i++) {\r
-    var entry = noteholder.childNodes[i];\r
-    if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")\r
-      entriesToRemove.push(entry);\r
-  }\r
-  for (i = 0; i < entriesToRemove.length; i++) {\r
-    noteholder.removeChild(entriesToRemove[i]);\r
-  }\r
-\r
-  // Rebuild footnote entries.\r
-  var cont = document.getElementById("content");\r
-  var spans = cont.getElementsByTagName("span");\r
-  var refs = {};\r
-  var n = 0;\r
-  for (i=0; i<spans.length; i++) {\r
-    if (spans[i].className == "footnote") {\r
-      n++;\r
-      var note = spans[i].getAttribute("data-note");\r
-      if (!note) {\r
-        // Use [\s\S] in place of . so multi-line matches work.\r
-        // Because JavaScript has no s (dotall) regex flag.\r
-        note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];\r
-        spans[i].innerHTML =\r
-          "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +\r
-          "' title='View footnote' class='footnote'>" + n + "</a>]";\r
-        spans[i].setAttribute("data-note", note);\r
-      }\r
-      noteholder.innerHTML +=\r
-        "<div class='footnote' id='_footnote_" + n + "'>" +\r
-        "<a href='#_footnoteref_" + n + "' title='Return to text'>" +\r
-        n + "</a>. " + note + "</div>";\r
-      var id =spans[i].getAttribute("id");\r
-      if (id != null) refs["#"+id] = n;\r
-    }\r
-  }\r
-  if (n == 0)\r
-    noteholder.parentNode.removeChild(noteholder);\r
-  else {\r
-    // Process footnoterefs.\r
-    for (i=0; i<spans.length; i++) {\r
-      if (spans[i].className == "footnoteref") {\r
-        var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");\r
-        href = href.match(/#.*/)[0];  // Because IE return full URL.\r
-        n = refs[href];\r
-        spans[i].innerHTML =\r
-          "[<a href='#_footnote_" + n +\r
-          "' title='View footnote' class='footnote'>" + n + "</a>]";\r
-      }\r
-    }\r
-  }\r
-},\r
-\r
-install: function(toclevels) {\r
-  var timerId;\r
-\r
-  function reinstall() {\r
-    asciidoc.footnotes();\r
-    if (toclevels) {\r
-      asciidoc.toc(toclevels);\r
-    }\r
-  }\r
-\r
-  function reinstallAndRemoveTimer() {\r
-    clearInterval(timerId);\r
-    reinstall();\r
-  }\r
-\r
-  timerId = setInterval(reinstall, 500);\r
-  if (document.addEventListener)\r
-    document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);\r
-  else\r
-    window.onload = reinstallAndRemoveTimer;\r
-}\r
-\r
-}\r
-asciidoc.install(2);\r
-/*]]>*/\r
-</script>\r
-</head>\r
-<body class="article">\r
-<div id="header">\r
-<h1>ccache README</h1>\r
-<span id="revnumber">version 3.2.9</span>\r
-<div id="toc">
-  <div id="toctitle">Table of Contents</div>
-  <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
-</div>\r
-</div>\r
-<div id="content">\r
-<div class="sect1">\r
-<h2 id="_about">About</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache is a compiler cache. It speeds up recompilation by caching the result of\r
-previous compilations and detecting when the same compilation is being done\r
-again. Supported languages are C, C<code>, Objective-C and Objective-C</code>.</p></div>\r
-<div class="paragraph"><p>Please see the manual page and documentation at <a href="http://ccache.samba.org">http://ccache.samba.org</a> for\r
-more information.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_documentation">Documentation</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>See the ccache(1) man page. It&#8217;s also available as MANUAL.{txt,html}.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_installation">Installation</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>See INSTALL.{txt.html}.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_web_site">Web site</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>The main ccache web site is here:</p></div>\r
-<div class="literalblock">\r
-<div class="content">\r
-<pre><code>http://ccache.samba.org</code></pre>\r
-</div></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_mailing_list">Mailing list</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>There is a mailing list for discussing usage and development of ccache:</p></div>\r
-<div class="literalblock">\r
-<div class="content">\r
-<pre><code>http://lists.samba.org/mailman/listinfo/ccache/</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>Anyone is welcome to join.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_bug_reports">Bug reports</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>To submit a bug report or to search for existing reports, please visit this web\r
-page:</p></div>\r
-<div class="literalblock">\r
-<div class="content">\r
-<pre><code>http://ccache.samba.org/bugs.html</code></pre>\r
-</div></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_source_code_repository">Source code repository</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>To get the very latest version of ccache directly from the source code\r
-repository, use git:</p></div>\r
-<div class="literalblock">\r
-<div class="content">\r
-<pre><code>git clone https://github.com/ccache/ccache.git</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>You can also browse the repository:</p></div>\r
-<div class="literalblock">\r
-<div class="content">\r
-<pre><code>https://github.com/ccache/ccache</code></pre>\r
-</div></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_history">History</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache was originally written by Andrew Tridgell and is currently developed and\r
-maintained by Joel Rosdahl. ccache started out as a reimplementation of Erik\r
-Thiele&#8217;s &#8220;compilercache&#8221; (see <a href="http://www.erikyyy.de/compilercache/">http://www.erikyyy.de/compilercache/</a>) in C.</p></div>\r
-<div class="paragraph"><p>See also NEWS.{txt,html}.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_license_and_copyright">License and copyright</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>See LICENSE.{txt,html} and AUTHORS.{txt,html}.</p></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div id="footnotes"><hr /></div>\r
-<div id="footer">\r
-<div id="footer-text">\r
-Version 3.2.9<br />\r
-Last updated\r
- 2016-09-28 21:28:19 CEST\r
-</div>\r
-</div>\r
-</body>\r
-</html>\r
similarity index 52%
rename from README.txt
rename to README.md
index bf808d4..cbb985a 100644 (file)
+++ b/README.md
@@ -1,6 +1,7 @@
-ccache README
-=============
+ccache
+======
 
+[![Build Status](https://travis-ci.org/ccache/ccache.svg?branch=master)](https://travis-ci.org/ccache/ccache)
 
 About
 -----
@@ -9,20 +10,17 @@ ccache is a compiler cache. It speeds up recompilation by caching the result of
 previous compilations and detecting when the same compilation is being done
 again. Supported languages are C, C++, Objective-C and Objective-C++.
 
-Please see the manual page and documentation at http://ccache.samba.org for
-more information.
-
 
 Documentation
 -------------
 
-See the ccache(1) man page. It's also available as MANUAL.{txt,html}.
+See the https://ccache.samba.org.
 
 
 Installation
 ------------
 
-See INSTALL.{txt.html}.
+See [INSTALL.md](INSTALL.md).
 
 
 Web site
@@ -30,7 +28,7 @@ Web site
 
 The main ccache web site is here:
 
-    http://ccache.samba.org
+https://ccache.samba.org
 
 
 Mailing list
@@ -38,7 +36,7 @@ Mailing list
 
 There is a mailing list for discussing usage and development of ccache:
 
-    http://lists.samba.org/mailman/listinfo/ccache/
+https://lists.samba.org/mailman/listinfo/ccache/
 
 Anyone is welcome to join.
 
@@ -49,20 +47,7 @@ Bug reports
 To submit a bug report or to search for existing reports, please visit this web
 page:
 
-    http://ccache.samba.org/bugs.html
-
-
-Source code repository
-----------------------
-
-To get the very latest version of ccache directly from the source code
-repository, use git:
-
-    git clone https://github.com/ccache/ccache.git
-
-You can also browse the repository:
-
-    https://github.com/ccache/ccache
+https://ccache.samba.org/bugs.html
 
 
 History
@@ -70,12 +55,12 @@ History
 
 ccache was originally written by Andrew Tridgell and is currently developed and
 maintained by Joel Rosdahl. ccache started out as a reimplementation of Erik
-Thiele's ``compilercache'' (see http://www.erikyyy.de/compilercache/) in C.
+Thiele's "compilercache" (see http://www.erikyyy.de/compilercache/) in C.
 
-See also NEWS.{txt,html}.
+See also https://ccache.samba.org/news.html.
 
 
 License and copyright
 ---------------------
 
-See LICENSE.{txt,html} and AUTHORS.{txt,html}.
+See https://ccache.samba.org/license.html.
diff --git a/args.c b/args.c
index a588484..66cc680 100644 (file)
--- a/args.c
+++ b/args.c
@@ -1,33 +1,30 @@
-/*
- * Copyright (C) 2002 Andrew Tridgell
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2002 Andrew Tridgell
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
 struct args *
 args_init(int init_argc, char **init_args)
 {
-       struct args *args;
-       int i;
-       args = (struct args *)x_malloc(sizeof(struct args));
+       struct args *args = (struct args *)x_malloc(sizeof(struct args));
        args->argc = 0;
        args->argv = (char **)x_malloc(sizeof(char *));
        args->argv[0] = NULL;
-       for (i = 0; i < init_argc; i++) {
+       for (int i = 0; i < init_argc; i++) {
                args_add(args, init_args[i]);
        }
        return args;
@@ -36,12 +33,10 @@ args_init(int init_argc, char **init_args)
 struct args *
 args_init_from_string(const char *command)
 {
-       struct args *args;
        char *p = x_strdup(command);
        char *q = p;
        char *word, *saveptr = NULL;
-
-       args = args_init(0, NULL);
+       struct args *args = args_init(0, NULL);
        while ((word = strtok_r(q, " \t\r\n", &saveptr))) {
                args_add(args, word);
                q = NULL;
@@ -54,22 +49,19 @@ args_init_from_string(const char *command)
 struct args *
 args_init_from_gcc_atfile(const char *filename)
 {
-       struct args *args;
-       char *pos, *argtext, *argpos, *argbuf;
-       char quoting;
-
-       /* Used to track quoting state; if \0, we are not inside quotes. Otherwise
-        * stores the quoting character that started it, for matching the end
-        * quote */
-       quoting = '\0';
-
-       if (!(argtext = read_text_file(filename, 0)))
+       char *argtext;
+       if (!(argtext = read_text_file(filename, 0))) {
                return NULL;
+       }
+
+       struct args *args = args_init(0, NULL);
+       char *pos = argtext;
+       char *argbuf = x_malloc(strlen(argtext) + 1);
+       char *argpos = argbuf;
 
-       args = args_init(0, NULL);
-       pos = argtext;
-       argbuf = x_malloc(strlen(argtext) + 1);
-       argpos = argbuf;
+       // Used to track quoting state; if \0, we are not inside quotes. Otherwise
+       // stores the quoting character that started it, for matching the end quote.
+       char quoting = '\0';
 
        while (1) {
                switch (*pos) {
@@ -103,13 +95,14 @@ args_init_from_gcc_atfile(const char *filename)
                        if (quoting) {
                                break;
                        }
-               /* Fall through */
+               // Fall through.
 
                case '\0':
-                       /* end of token */
+                       // End of token
                        *argpos = '\0';
-                       if (argbuf[0] != '\0')
+                       if (argbuf[0] != '\0') {
                                args_add(args, argbuf);
+                       }
                        argpos = argbuf;
                        if (*pos == '\0') {
                                goto out;
@@ -136,22 +129,18 @@ args_copy(struct args *args)
        return args_init(args->argc, args->argv);
 }
 
-/* Insert all arguments in src into dest at position index.
- * If replace is true, the element at dest->argv[index] is replaced
- * with the contents of src and everything past it is shifted.
- * Otherwise, dest->argv[index] is also shifted.
- *
- * src is consumed by this operation and should not be freed or used
- * again by the caller */
+// Insert all arguments in src into dest at position index. If replace is true,
+// the element at dest->argv[index] is replaced with the contents of src and
+// everything past it is shifted. Otherwise, dest->argv[index] is also shifted.
+//
+// src is consumed by this operation and should not be freed or used again by
+// the caller.
 void
 args_insert(struct args *dest, int index, struct args *src, bool replace)
 {
-       int offset;
-       int i;
-
-       /* Adjustments made if we are replacing or shifting the element
-        * currently at dest->argv[index] */
-       offset = replace ? 1 : 0;
+       // Adjustments made if we are replacing or shifting the element currently at
+       // dest->argv[index].
+       int offset = replace ? 1 : 0;
 
        if (replace) {
                free(dest->argv[index]);
@@ -159,9 +148,9 @@ args_insert(struct args *dest, int index, struct args *src, bool replace)
 
        if (src->argc == 0) {
                if (replace) {
-                       /* Have to shift everything down by 1 since
-                        * we replaced with an empty list */
-                       for (i = index; i < dest->argc; i++) {
+                       // Have to shift everything down by 1 since we replaced with an empty
+                       // list.
+                       for (int i = index; i < dest->argc; i++) {
                                dest->argv[i] = dest->argv[i + 1];
                        }
                        dest->argc--;
@@ -171,7 +160,7 @@ args_insert(struct args *dest, int index, struct args *src, bool replace)
        }
 
        if (src->argc == 1 && replace) {
-               /* Trivial case; replace with 1 element */
+               // Trivial case; replace with 1 element.
                dest->argv[index] = src->argv[0];
                src->argc = 0;
                args_free(src);
@@ -183,13 +172,13 @@ args_insert(struct args *dest, int index, struct args *src, bool replace)
          (src->argc + dest->argc + 1 - offset) *
          sizeof(char *));
 
-       /* Shift arguments over */
-       for (i = dest->argc; i >= index + offset; i--) {
+       // Shift arguments over.
+       for (int i = dest->argc; i >= index + offset; i--) {
                dest->argv[i + src->argc - offset] = dest->argv[i];
        }
 
-       /* Copy the new arguments into place */
-       for (i = 0; i < src->argc; i++) {
+       // Copy the new arguments into place.
+       for (int i = 0; i < src->argc; i++) {
                dest->argv[i + index] = src->argv[i];
        }
 
@@ -201,11 +190,10 @@ args_insert(struct args *dest, int index, struct args *src, bool replace)
 void
 args_free(struct args *args)
 {
-       int i;
        if (!args) {
                return;
        }
-       for (i = 0; i < args->argc; ++i) {
+       for (int i = 0; i < args->argc; ++i) {
                if (args->argv[i]) {
                        free(args->argv[i]);
                }
@@ -224,17 +212,16 @@ args_add(struct args *args, const char *s)
        args->argv[args->argc] = NULL;
 }
 
-/* Add all arguments in to_append to args. */
+// Add all arguments in to_append to args.
 void
 args_extend(struct args *args, struct args *to_append)
 {
-       int i;
-       for (i = 0; i < to_append->argc; i++) {
+       for (int i = 0; i < to_append->argc; i++) {
                args_add(args, to_append->argv[i]);
        }
 }
 
-/* pop the last element off the args list */
+// Pop the last element off the args list.
 void
 args_pop(struct args *args, int n)
 {
@@ -245,7 +232,7 @@ args_pop(struct args *args, int n)
        }
 }
 
-/* set argument at given index */
+// Set argument at given index.
 void
 args_set(struct args *args, int index, const char *value)
 {
@@ -254,7 +241,7 @@ args_set(struct args *args, int index, const char *value)
        args->argv[index] = x_strdup(value);
 }
 
-/* remove the first element of the argument list */
+// Remove the first element of the argument list.
 void
 args_remove_first(struct args *args)
 {
@@ -263,7 +250,7 @@ args_remove_first(struct args *args)
        args->argc--;
 }
 
-/* add an argument into the front of the argument list */
+// Add an argument into the front of the argument list.
 void
 args_add_prefix(struct args *args, const char *s)
 {
@@ -275,12 +262,11 @@ args_add_prefix(struct args *args, const char *s)
        args->argc++;
 }
 
-/* strip any arguments beginning with the specified prefix */
+// Strip any arguments beginning with the specified prefix.
 void
 args_strip(struct args *args, const char *prefix)
 {
-       int i;
-       for (i = 0; i < args->argc; ) {
+       for (int i = 0; i < args->argc; ) {
                if (str_startswith(args->argv[i], prefix)) {
                        free(args->argv[i]);
                        memmove(&args->argv[i],
@@ -293,42 +279,36 @@ args_strip(struct args *args, const char *prefix)
        }
 }
 
-/*
- * Format args to a space-separated string. Does not quote spaces. Caller
- * frees.
- */
+// Format args to a space-separated string. Does not quote spaces. Caller
+// frees.
 char *
 args_to_string(struct args *args)
 {
-       char *result;
-       char **p;
        unsigned size = 0;
-       int pos;
-       for (p = args->argv; *p; p++) {
+       for (char **p = args->argv; *p; p++) {
                size += strlen(*p) + 1;
        }
-       result = x_malloc(size + 1);
-       pos = 0;
-       for (p = args->argv; *p; p++) {
+
+       char *result = x_malloc(size + 1);
+       int pos = 0;
+       for (char **p = args->argv; *p; p++) {
                pos += sprintf(&result[pos], "%s ", *p);
        }
        result[pos - 1] = '\0';
        return result;
 }
 
-/* Returns true if args1 equals args2, else false. */
+// Returns true if args1 equals args2, else false.
 bool
 args_equal(struct args *args1, struct args *args2)
 {
-       int i;
        if (args1->argc != args2->argc) {
                return false;
        }
-       for (i = 0; i < args1->argc; i++) {
+       for (int i = 0; i < args1->argc; i++) {
                if (!str_eq(args1->argv[i], args2->argv[i])) {
                        return false;
                }
        }
        return true;
 }
-
index c3ea6d7..f6444c4 100644 (file)
--- a/ccache.1
+++ b/ccache.1
@@ -2,12 +2,12 @@
 .\"     Title: ccache
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\"      Date: 09/28/2016
+.\"      Date: 08/27/2016
 .\"    Manual: ccache Manual
-.\"    Source: ccache 3.2.9
+.\"    Source: ccache 3.3
 .\"  Language: English
 .\"
-.TH "CCACHE" "1" "09/28/2016" "ccache 3\&.2\&.9" "ccache Manual"
+.TH "CCACHE" "1" "08/27/2016" "ccache 3\&.3" "ccache Manual"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -40,7 +40,7 @@ ccache \- a fast C/C++ compiler cache
 .sp
 ccache is a compiler cache\&. It speeds up recompilation by caching the result of previous compilations and detecting when the same compilation is being done again\&. Supported languages are C, C++, Objective\-C and Objective\-C++\&.
 .sp
-ccache has been carefully written to always produce exactly the same compiler output that you would get without the cache\&. The only way you should be able to tell that you are using ccache is the speed\&. Currently known exceptions to this goal are listed under BUGS\&. If you ever discover an undocumented case where ccache changes the output of your compiler, please let us know\&.
+ccache has been carefully written to always produce exactly the same compiler output that you would get without the cache\&. The only way you should be able to tell that you are using ccache is the speed\&. Currently known exceptions to this goal are listed under CAVEATS\&. If you ever discover an undocumented case where ccache changes the output of your compiler, please let us know\&.
 .SS "Features"
 .sp
 .RS 4
@@ -356,7 +356,9 @@ Below is a list of available configuration settings\&. The corresponding environ
 \fBbase_dir\fR (\fBCCACHE_BASEDIR\fR)
 .RS 4
 This setting should be an absolute path to a directory\&. ccache then rewrites absolute paths into relative paths before computing the hash that identifies the compilation, but only for paths under the specified directory\&. If set to the empty string (which is the default), no rewriting is done\&. See also the discussion under
-COMPILING IN DIFFERENT DIRECTORIES\&.
+COMPILING IN DIFFERENT DIRECTORIES\&. If using GCC or newer versions of Clang, you might want to look into the
+\fB\-fdebug\-prefix\-map=old=new\fR
+option for relocating debug info to a common prefix (mapping prefix with old=new)\&.
 .RE
 .PP
 \fBcache_dir\fR (\fBCCACHE_DIR\fR)
@@ -479,7 +481,7 @@ When true, ccache will just call the real compiler, bypassing the cache complete
 .PP
 \fBextra_files_to_hash\fR (\fBCCACHE_EXTRAFILES\fR)
 .RS 4
-This setting is a list of paths to files that ccache will include in the the hash sum that idetifies the build\&. The list separator is semicolon on Windows systems and colon on other systems\&.
+This setting is a list of paths to files that ccache will include in the the hash sum that identifies the build\&. The list separator is semicolon on Windows systems and colon on other systems\&.
 .RE
 .PP
 \fBhard_link\fR (\fBCCACHE_HARDLINK\fR or \fBCCACHE_NOHARDLINK\fR, see Boolean values above)
@@ -489,7 +491,56 @@ If true, ccache will attempt to use hard links from the cache directory when cre
 .PP
 \fBhash_dir\fR (\fBCCACHE_HASHDIR\fR or \fBCCACHE_NOHASHDIR\fR, see Boolean values above)
 .RS 4
-If true, ccache will include the current working directory in the hash that is used to distinguish two compilations\&. This prevents a problem with the storage of the current working directory in the debug info of an object file, which can lead ccache to give a cached object file that has the working directory in the debug info set incorrectly\&. This option is off by default as the incorrect setting of this debug info rarely causes problems\&. If you strike problems with GDB not using the correct directory then enable this option\&.
+If true (which is the default), ccache will include the current working directory (CWD) in the hash that is used to distinguish two compilations when generating debug info (compiler option
+\fB\-g\fR
+with variations)\&. Exception: The CWD will not be included in the hash if
+\fBbase_dir\fR
+is set (and matches the CWD) and the compiler option
+\fB\-fdebug\-prefix\-map\fR
+is used\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+The reason for including the CWD in the hash by default is to prevent a
+problem with the storage of the current working directory in the debug info
+of an object file, which can lead ccache to return a cached object file
+that has the working directory in the debug info set incorrectly\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+You can disable this setting to get cache hits when compiling the same
+source code in different directories if you don\*(Aqt mind that CWD in the
+debug info might be incorrect\&.
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.PP
+\fBignore_headers_in_manifest\fR (\fBCCACHE_IGNOREHEADERS\fR)
+.RS 4
+This setting is a list of paths to files (or directories with headers) that ccache will
+\fBnot\fR
+include in the manifest list that makes up the direct mode\&. Note that this can cause stale cache hits if those headers do indeed change\&. The list separator is semicolon on Windows systems and colon on other systems\&.
+.RE
+.PP
+\fBkeep_comments_cpp\fR (\fBCCACHE_COMMENTS\fR or \fBCCACHE_NOCOMMENTS\fR, see Boolean values above)
+.RS 4
+If true, ccache will not discard the comments before hashing preprocessor output\&. This can be used to check documentation with
+\fB\-Wdocumentation\fR\&.
+.RE
+.PP
+\fBlimit_multiple\fR (\fBCCACHE_LIMIT_MULTIPLE\fR)
+.RS 4
+Sets the limit when cleaning up\&. Files are deleted (in LRU order) until the levels are below the limit\&. The default is 0\&.8 (= 80%)\&.
 .RE
 .PP
 \fBlog_file\fR (\fBCCACHE_LOGFILE\fR)
@@ -520,6 +571,11 @@ This option adds a list of prefixes (separated by space) to the command line tha
 USING CCACHE WITH OTHER COMPILER WRAPPERS\&.
 .RE
 .PP
+\fBprefix_command_cpp\fR (\fBCCACHE_PREFIX_CPP\fR)
+.RS 4
+This option adds a list of prefixes (separated by space) to the command line that ccache uses when invoking the preprocessor\&.
+.RE
+.PP
 \fBread_only\fR (\fBCCACHE_READONLY\fR or \fBCCACHE_NOREADONLY\fR, see Boolean values above)
 .RS 4
 If true, ccache will attempt to use existing cached object files, but it will not to try to add anything new to the cache\&. If you are using this because your ccache directory is read\-only, then you need to set
@@ -543,7 +599,23 @@ If true, ccache will not use any previously stored result\&. New results will st
 .PP
 \fBrun_second_cpp\fR (\fBCCACHE_CPP2\fR or \fBCCACHE_NOCPP2\fR, see Boolean values above)
 .RS 4
-If true, ccache will not use the optimisation of avoiding the second call to the preprocessor by compiling the preprocessed output that was used for finding the hash in the case of a cache miss\&. This is primarily a debugging option, although it is possible that some unusual compilers will have problems with compiling the preprocessed output, in which case this option could allow ccache to be used anyway\&.
+If true, ccache will first run the preprocessor to preprocess the source code (see
+THE PREPROCESSOR MODE) and then on a cache miss run the compiler on the source code to get hold of the object file\&. This is the default\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+If false, ccache will first run preprocessor to preprocess the source code
+and then on a cache miss run the compiler on the _preprocessed source code_
+instead of the original source code\&. This makes cache misses slightly
+faster since the source code only has to be preprocessed once\&. The downside
+is that some compilers won\*(Aqt produce the same result (for instance
+diagnostics warnings) when compiling preprocessed source code\&.
+.fi
+.if n \{\
+.RE
+.\}
 .RE
 .PP
 \fBsloppiness\fR (\fBCCACHE_SLOPPINESS\fR)
@@ -572,6 +644,11 @@ By default, ccache also will not cache a file if it includes a header whose ctim
 By default, ccache will not cache a file if it includes a header whose mtime is too new\&. This option disables that check\&.
 .RE
 .PP
+\fBno_system_headers\fR
+.RS 4
+By default, ccache will also include all system headers in the manifest\&. With this option set, ccache will only include system headers in the hash but not add the system header files to the list of include files\&.
+.RE
+.PP
 \fBpch_defines\fR
 .RS 4
 Be sloppy about #defines when precompiling a header file\&. See
@@ -866,9 +943,10 @@ a compiler option not supported by the direct mode is used:
 a
 \fB\-Wp,\fR\fB\fIX\fR\fR
 compiler option other than
-\fB\-Wp,\-MD,\fR\fB\fIpath\fR\fR
-and
+\fB\-Wp,\-MD,\fR\fB\fIpath\fR\fR,
 \fB\-Wp,\-MMD,\fR\fB\fIpath\fR\fR
+and
+\fB\-Wp,\-D_define_\fR
 .RE
 .sp
 .RS 4
@@ -1214,8 +1292,8 @@ Compiler upgrades will not be detected properly\&.
 The cached results will not be shared between compilations with and without the other wrapper\&.
 .RE
 .sp
-Another minor thing is that if \fBprefix_command\fR is used, ccache will not invoke the other wrapper when running the preprocessor, which increase performance\&.
-.SH "BUGS"
+Another minor thing is that if \fBprefix_command\fR is used, ccache will not invoke the other wrapper when running the preprocessor, which increases performance\&. You can use the \fBprefix_command_cpp\fR configuration setting if you also want to invoke the other wrapper when doing preprocessing (normally by adding \fB\-E\fR)\&.
+.SH "CAVEATS"
 .sp
 .RS 4
 .ie n \{\
@@ -1254,7 +1332,7 @@ A general tip for getting information about what ccache is doing is to enable de
 .sp
 ccache has been written to perform well out of the box, but sometimes you may have to do some adjustments of how you use the compiler and ccache in order to improve performance\&.
 .sp
-Since ccache works best when I/O is fast, put the cache directory on a fast storage device if possible\&. Having lots of free memory so that files in the cache directory stay in the disk cache is also preferrable\&.
+Since ccache works best when I/O is fast, put the cache directory on a fast storage device if possible\&. Having lots of free memory so that files in the cache directory stay in the disk cache is also preferable\&.
 .sp
 A good way of monitoring how well ccache works is to run \fBccache \-s\fR before and after your build and then compare the statistics counters\&. Here are some common problems and what may be done to increase the hit rate:
 .sp
@@ -1305,9 +1383,9 @@ The compiler option
 or
 \fB\-Wp,\fR\fB\fIX\fR\fR
 (except
-\fB\-Wp,\-MD,\fR\fB\fIpath\fR\fR
-and
-\fBWp,\-MMD,\fR\fB\fIpath\fR\fR) is used\&.
+\fB\-Wp,\-MD,\fR\fB\fIpath\fR\fR,
+\fB\-Wp,\-MMD,\fR\fB\fIpath\fR\fR, and
+\fB\-Wp,\-D_define_\fR) is used\&.
 .RE
 .sp
 .RS 4
@@ -1468,9 +1546,6 @@ for how to remedy this\&.
 If \(lqcan\(cqt use precompiled header\(rq has been incremented, see
 PRECOMPILED HEADERS\&.
 .RE
-.SS "Errors when compiling with ccache"
-.sp
-If compilation doesn\(cqt work with ccache, but it works without it, one possible reason is that the compiler can\(cqt compile preprocessed output correctly\&. A workaround that may work is to enable \fBrun_second_cpp\fR*\&. This will make cache misses slower, though, so it is better to find and fix the root cause\&.
 .SS "Corrupt object files"
 .sp
 It should be noted that ccache is susceptible to general storage problems\&. If a bad object file sneaks into the cache for some reason, it will of course stay bad\&. Some possible reasons for erroneous object files are bad hardware (disk drive, disk controller, memory, etc), buggy drivers or file systems, a bad \fBprefix_command\fR or compiler wrapper\&. If this happens, the easiest way of fixing it is this:
@@ -1515,7 +1590,7 @@ An alternative is to clear the whole cache with \fBccache \-C\fR if you don\(cqt
 There are no reported issues about ccache producing broken object files reproducibly\&. That doesn\(cqt mean it can\(cqt happen, so if you find a repeatable case, please report it\&.
 .SH "MORE INFORMATION"
 .sp
-Credits, mailing list information, bug reporting instructions, source code, etc, can be found on ccache\(cqs web site: http://ccache\&.samba\&.org\&.
+Credits, mailing list information, bug reporting instructions, source code, etc, can be found on ccache\(cqs web site: https://ccache\&.samba\&.org\&.
 .SH "AUTHOR"
 .sp
-ccache was originally written by Andrew Tridgell and is currently developed and maintained by Joel Rosdahl\&. See AUTHORS\&.txt or AUTHORS\&.html and http://ccache\&.samba\&.org/credits\&.html for a list of contributors\&.
+ccache was originally written by Andrew Tridgell and is currently developed and maintained by Joel Rosdahl\&. See AUTHORS\&.txt or AUTHORS\&.html and https://ccache\&.samba\&.org/credits\&.html for a list of contributors\&.
index ddbc822..1af1b04 100644 (file)
--- a/ccache.c
+++ b/ccache.c
@@ -1,23 +1,21 @@
-/*
- * ccache -- a fast C/C++ compiler cache
- *
- * Copyright (C) 2002-2007 Andrew Tridgell
- * Copyright (C) 2009-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// ccache -- a fast C/C++ compiler cache
+//
+// Copyright (C) 2002-2007 Andrew Tridgell
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 #include "compopt.h"
@@ -69,161 +67,148 @@ static const char USAGE_TEXT[] =
   "    -h, --help            print this help text\n"
   "    -V, --version         print version and copyright information\n"
   "\n"
-  "See also <http://ccache.samba.org>.\n";
+  "See also <https://ccache.samba.org>.\n";
 
-/* Global configuration data. */
+// Global configuration data.
 struct conf *conf = NULL;
 
-/* Where to write configuration changes. */
+// Where to write configuration changes.
 char *primary_config_path = NULL;
 
-/* Secondary, read-only configuration file (if any). */
+// Secondary, read-only configuration file (if any).
 char *secondary_config_path = NULL;
 
-/* current working directory taken from $PWD, or getcwd() if $PWD is bad */
+// Current working directory taken from $PWD, or getcwd() if $PWD is bad.
 char *current_working_dir = NULL;
 
-/* the original argument list */
+// The original argument list.
 static struct args *orig_args;
 
-/* the source file */
+// The source file.
 static char *input_file;
 
-/* The output file being compiled to. */
+// The output file being compiled to.
 static char *output_obj;
 
-/* The path to the dependency file (implicit or specified with -MF). */
+// The path to the dependency file (implicit or specified with -MF).
 static char *output_dep;
 
-/* The path to the coverage file (implicit when using -ftest-coverage). */
+// The path to the coverage file (implicit when using -ftest-coverage).
 static char *output_cov;
 
-/* Diagnostic generation information (clang). Contains pathname if not
- * NULL. */
+// Diagnostic generation information (clang). Contains pathname if not NULL.
 static char *output_dia = NULL;
 
-/* -gsplit-dwarf support: Split dwarf information (GCC 4.8 and
- *  up). Contains pathname if not NULL. */
+// Split dwarf information (GCC 4.8 andup). Contains pathname if not NULL.
 static char *output_dwo = NULL;
 
-/*
- * Name (represented as a struct file_hash) of the file containing the cached
- * object code.
- */
+// Array for storing -arch options.
+#define MAX_ARCH_ARGS 10
+static size_t arch_args_size = 0;
+static char *arch_args[MAX_ARCH_ARGS] = {NULL};
+
+// Name (represented as a struct file_hash) of the file containing the cached
+// object code.
 static struct file_hash *cached_obj_hash;
 
-/*
- * Full path to the file containing the cached object code
- * (cachedir/a/b/cdef[...]-size.o).
- */
+// Full path to the file containing the cached object code
+// (cachedir/a/b/cdef[...]-size.o).
 static char *cached_obj;
 
-/*
- * Full path to the file containing the standard error output
- * (cachedir/a/b/cdef[...]-size.stderr).
- */
+// Full path to the file containing the standard error output
+// (cachedir/a/b/cdef[...]-size.stderr).
 static char *cached_stderr;
 
-/*
- * Full path to the file containing the dependency information
- * (cachedir/a/b/cdef[...]-size.d).
- */
+// Full path to the file containing the dependency information
+// (cachedir/a/b/cdef[...]-size.d).
 static char *cached_dep;
 
-/*
- * Full path to the file containing the coverage information
- * (cachedir/a/b/cdef[...]-size.gcno).
- */
+// Full path to the file containing the coverage information
+// (cachedir/a/b/cdef[...]-size.gcno).
 static char *cached_cov;
 
-/*
- * Full path to the file containing the diagnostic information (for clang)
- * (cachedir/a/b/cdef[...]-size.dia).
- */
+// Full path to the file containing the diagnostic information (for clang)
+// (cachedir/a/b/cdef[...]-size.dia).
 static char *cached_dia;
 
-/*
- * -gsplit-dwarf support:
- * Full path to the file containing the split dwarf (for GCC 4.8 and
- * above)
- * (cachedir/a/b/cdef[...]-size.dwo).
- *
- * contains NULL if -gsplit-dwarf is not given.
- */
+// Full path to the file containing the split dwarf (for GCC 4.8 and above)
+// (cachedir/a/b/cdef[...]-size.dwo).
+//
+// Contains NULL if -gsplit-dwarf is not given.
 static char *cached_dwo;
 
-/*
- * -gsplit-dwarf support:
- * using_split_dwarf is true if "-gsplit-dwarf" is given to the
- * compiler (GCC 4.8 and up).
- */
+// using_split_dwarf is true if "-gsplit-dwarf" is given to the compiler (GCC
+// 4.8 and up).
 bool using_split_dwarf = false;
 
-/*
- * Full path to the file containing the manifest
- * (cachedir/a/b/cdef[...]-size.manifest).
- */
+// Full path to the file containing the manifest
+// (cachedir/a/b/cdef[...]-size.manifest).
 static char *manifest_path;
 
-/*
- * Time of compilation. Used to see if include files have changed after
- * compilation.
- */
+// Time of compilation. Used to see if include files have changed after
+// compilation.
 time_t time_of_compilation;
 
-/*
- * Files included by the preprocessor and their hashes/sizes. Key: file path.
- * Value: struct file_hash.
- */
-static struct hashtable *included_files;
+// Files included by the preprocessor and their hashes/sizes. Key: file path.
+// Value: struct file_hash.
+static struct hashtable *included_files = NULL;
+
+// Uses absolute path for some include files.
+static bool has_absolute_include_headers = false;
+
+// List of headers to ignore.
+static char **ignore_headers;
 
-/* is gcc being asked to output dependencies? */
+// Size of headers to ignore list.
+static size_t ignore_headers_len;
+
+// Is the compiler being asked to output debug info?
+static bool generating_debuginfo;
+
+// Is the compiler being asked to output dependencies?
 static bool generating_dependencies;
 
-/* is gcc being asked to output coverage? */
+// Is the compiler being asked to output coverage?
 static bool generating_coverage;
 
-/* is gcc being asked to output coverage data (.gcda) at runtime? */
+// Relocating debuginfo in the format old=new.
+static char *debug_prefix_map = NULL;
+
+// Is the compiler being asked to output coverage data (.gcda) at runtime?
 static bool profile_arcs;
 
-/* name of the custom profile directory (default: object dirname) */
+// Name of the custom profile directory (default: object dirname).
 static char *profile_dir;
 
-/* the name of the temporary pre-processor file */
+// The name of the temporary preprocessed file.
 static char *i_tmpfile;
 
-/* are we compiling a .i or .ii file directly? */
+// Are we compiling a .i or .ii file directly?
 static bool direct_i_file;
 
-/* the name of the cpp stderr file */
+// The name of the cpp stderr file.
 static char *cpp_stderr;
 
-/*
- * Full path to the statistics file in the subdirectory where the cached result
- * belongs (<cache_dir>/<x>/stats).
- */
+// Full path to the statistics file in the subdirectory where the cached result
+// belongs (<cache_dir>/<x>/stats).
 char *stats_file = NULL;
 
-/* Whether the output is a precompiled header */
+// Whether the output is a precompiled header.
 static bool output_is_precompiled_header = false;
 
-/* Profile generation / usage information */
+// Profile generation / usage information.
 static char *profile_dir = NULL;
 static bool profile_use = false;
 static bool profile_generate = false;
 
-/*
- * Whether we are using a precompiled header (either via -include, #include or
- * clang's -include-pch or -include-pth).
- */
+// Whether we are using a precompiled header (either via -include, #include or
+// clang's -include-pch or -include-pth).
 static bool using_precompiled_header = false;
 
-/*
- * The .gch/.pch/.pth file used for compilation.
- */
+// The .gch/.pch/.pth file used for compilation.
 static char *included_pch_file = NULL;
 
-/* How long (in microseconds) to wait before breaking a stale lock. */
+// How long (in microseconds) to wait before breaking a stale lock.
 unsigned lock_staleness_limit = 2000000;
 
 enum fromcache_call_mode {
@@ -236,39 +221,35 @@ struct pending_tmp_file {
        struct pending_tmp_file *next;
 };
 
-/* Temporary files to remove at program exit. */
+// Temporary files to remove at program exit.
 static struct pending_tmp_file *pending_tmp_files = NULL;
 
+#ifndef _WIN32
 static sigset_t fatal_signal_set;
 
-/* PID of currently executing compiler that we have started, if any. 0 means no
- * ongoing compilation. */
+// PID of currently executing compiler that we have started, if any. 0 means no
+// ongoing compilation.
 static pid_t compiler_pid = 0;
+#endif
 
-/*
- * This is a string that identifies the current "version" of the hash sum
- * computed by ccache. If, for any reason, we want to force the hash sum to be
- * different for the same input in a new ccache version, we can just change
- * this string. A typical example would be if the format of one of the files
- * stored in the cache changes in a backwards-incompatible way.
- */
+// This is a string that identifies the current "version" of the hash sum
+// computed by ccache. If, for any reason, we want to force the hash sum to be
+// different for the same input in a new ccache version, we can just change
+// this string. A typical example would be if the format of one of the files
+// stored in the cache changes in a backwards-incompatible way.
 static const char HASH_PREFIX[] = "3";
 
 static void
-add_prefix(struct args *args)
+add_prefix(struct args *args, char *prefix_command)
 {
-       char *e;
-       char *tok, *saveptr = NULL;
-       struct args *prefix;
-       int i;
-
-       if (str_eq(conf->prefix_command, "")) {
+       if (str_eq(prefix_command, "")) {
                return;
        }
 
-       prefix = args_init(0, NULL);
-       e = x_strdup(conf->prefix_command);
-       for (tok = strtok_r(e, " ", &saveptr);
+       struct args *prefix = args_init(0, NULL);
+       char *e = x_strdup(prefix_command);
+       char *saveptr = NULL;
+       for (char *tok = strtok_r(e, " ", &saveptr);
             tok;
             tok = strtok_r(NULL, " ", &saveptr)) {
                char *p;
@@ -283,21 +264,21 @@ add_prefix(struct args *args)
        }
        free(e);
 
-       cc_log("Using command-line prefix %s", conf->prefix_command);
-       for (i = prefix->argc; i != 0; i--) {
+       cc_log("Using command-line prefix %s", prefix_command);
+       for (int i = prefix->argc; i != 0; i--) {
                args_add_prefix(args, prefix->argv[i-1]);
        }
        args_free(prefix);
 }
 
-/* Something went badly wrong - just execute the real compiler. */
+// Something went badly wrong - just execute the real compiler.
 static void
 failed(void)
 {
        assert(orig_args);
 
        args_strip(orig_args, "--ccache-");
-       add_prefix(orig_args);
+       add_prefix(orig_args, conf->prefix_command);
 
        cc_log("Failed; falling back to running the real compiler");
        cc_log_argv("Executing ", orig_args->argv);
@@ -311,7 +292,7 @@ temp_dir()
 {
        static char *path = NULL;
        if (path) {
-               return path; /* Memoize */
+               return path; // Memoize
        }
        path = conf->temporary_dir;
        if (str_eq(path, "")) {
@@ -323,24 +304,26 @@ temp_dir()
 void
 block_signals(void)
 {
+#ifndef _WIN32
        sigprocmask(SIG_BLOCK, &fatal_signal_set, NULL);
+#endif
 }
 
 void
 unblock_signals(void)
 {
+#ifndef _WIN32
        sigset_t empty;
        sigemptyset(&empty);
        sigprocmask(SIG_SETMASK, &empty, NULL);
+#endif
 }
 
 static void
 add_pending_tmp_file(const char *path)
 {
-       struct pending_tmp_file *e;
-
        block_signals();
-       e = x_malloc(sizeof(*e));
+       struct pending_tmp_file *e = x_malloc(sizeof(*e));
        e->path = x_strdup(path);
        e->next = pending_tmp_files;
        pending_tmp_files = e;
@@ -352,11 +335,11 @@ do_clean_up_pending_tmp_files(void)
 {
        struct pending_tmp_file *p = pending_tmp_files;
        while (p) {
-               /* Can't call tmp_unlink here since its cc_log calls aren't signal safe. */
+               // Can't call tmp_unlink here since its cc_log calls aren't signal safe.
                unlink(p->path);
                p = p->next;
-               /* Leak p->path and p here because clean_up_pending_tmp_files needs to be
-                * signal safe. */
+               // Leak p->path and p here because clean_up_pending_tmp_files needs to be
+               // signal safe.
        }
 }
 
@@ -368,15 +351,16 @@ clean_up_pending_tmp_files(void)
        unblock_signals();
 }
 
+#ifndef _WIN32
 static void
 signal_handler(int signum)
 {
-       /* Unregister handler for this signal so that we can send the signal to
-        * ourselves at the end of the handler. */
+       // Unregister handler for this signal so that we can send the signal to
+       // ourselves at the end of the handler.
        signal(signum, SIG_DFL);
 
-       /* If ccache was killed explicitly, then bring the compiler subprocess (if
-        * any) with us as well. */
+       // If ccache was killed explicitly, then bring the compiler subprocess (if
+       // any) with us as well.
        if (signum == SIGTERM
            && compiler_pid != 0
            && waitpid(compiler_pid, NULL, WNOHANG) == 0) {
@@ -386,12 +370,12 @@ signal_handler(int signum)
        do_clean_up_pending_tmp_files();
 
        if (compiler_pid != 0) {
-               /* Wait for compiler subprocess to exit before we snuff it. */
+               // Wait for compiler subprocess to exit before we snuff it.
                waitpid(compiler_pid, NULL, 0);
        }
 
-       /* Resend signal to ourselves to exit properly after returning from the
-        * handler. */
+       // Resend signal to ourselves to exit properly after returning from the
+       // handler.
        kill(getpid(), signum);
 }
 
@@ -430,35 +414,32 @@ set_up_signal_handlers(void)
        register_signal_handler(SIGQUIT);
 #endif
 }
+#endif // _WIN32
 
 static void
 clean_up_internal_tempdir(void)
 {
-       DIR *dir;
-       struct dirent *entry;
-       struct stat st;
        time_t now = time(NULL);
-
+       struct stat st;
        if (x_stat(conf->cache_dir, &st) != 0 || st.st_mtime + 3600 >= now) {
-               /* No cleanup needed. */
+               // No cleanup needed.
                return;
        }
 
        update_mtime(conf->cache_dir);
 
-       dir = opendir(temp_dir());
+       DIR *dir = opendir(temp_dir());
        if (!dir) {
                return;
        }
 
+       struct dirent *entry;
        while ((entry = readdir(dir))) {
-               char *path;
-
                if (str_eq(entry->d_name, ".") || str_eq(entry->d_name, "..")) {
                        continue;
                }
 
-               path = format("%s/%s", temp_dir(), entry->d_name);
+               char *path = format("%s/%s", temp_dir(), entry->d_name);
                if (x_lstat(path, &st) == 0 && st.st_mtime + 3600 < now) {
                        tmp_unlink(path);
                }
@@ -486,84 +467,97 @@ get_current_working_dir(void)
        return current_working_dir;
 }
 
-/*
- * Transform a name to a full path into the cache directory, creating needed
- * sublevels if needed. Caller frees.
- */
+// Transform a name to a full path into the cache directory, creating needed
+// sublevels if needed. Caller frees.
 static char *
 get_path_in_cache(const char *name, const char *suffix)
 {
-       unsigned i;
-       char *path;
-       char *result;
-
-       path = x_strdup(conf->cache_dir);
-       for (i = 0; i < conf->cache_dir_levels; ++i) {
+       char *path = x_strdup(conf->cache_dir);
+       for (unsigned i = 0; i < conf->cache_dir_levels; ++i) {
                char *p = format("%s/%c", path, name[i]);
                free(path);
                path = p;
        }
 
-       result = format("%s/%s%s", path, name + conf->cache_dir_levels, suffix);
+       char *result =
+         format("%s/%s%s", path, name + conf->cache_dir_levels, suffix);
        free(path);
        return result;
 }
 
-/*
- * This function hashes an include file and stores the path and hash in the
- * global included_files variable. If the include file is a PCH, cpp_hash is
- * also updated. Takes over ownership of path.
- */
+// This function hashes an include file and stores the path and hash in the
+// global included_files variable. If the include file is a PCH, cpp_hash is
+// also updated. Takes over ownership of path.
 static void
-remember_include_file(char *path, struct mdfour *cpp_hash)
+remember_include_file(char *path, struct mdfour *cpp_hash, bool system)
 {
-#ifdef _WIN32
-       DWORD attributes;
-#endif
-       struct mdfour fhash;
-       struct stat st;
-       char *source = NULL;
-       size_t size;
-       bool is_pch;
        size_t path_len = strlen(path);
-
        if (path_len >= 2 && (path[0] == '<' && path[path_len - 1] == '>')) {
-               /* Typically <built-in> or <command-line>. */
+               // Typically <built-in> or <command-line>.
                goto ignore;
        }
 
        if (str_eq(path, input_file)) {
-               /* Don't remember the input file. */
+               // Don't remember the input file.
+               goto ignore;
+       }
+
+       if (system && (conf->sloppiness & SLOPPY_NO_SYSTEM_HEADERS)) {
+               // Don't remember this system header.
                goto ignore;
        }
 
        if (hashtable_search(included_files, path)) {
-               /* Already known include file. */
+               // Already known include file.
                goto ignore;
        }
 
 #ifdef _WIN32
-       /* stat fails on directories on win32 */
-       attributes = GetFileAttributes(path);
+       // stat fails on directories on win32.
+       DWORD attributes = GetFileAttributes(path);
        if (attributes != INVALID_FILE_ATTRIBUTES &&
-           attributes & FILE_ATTRIBUTE_DIRECTORY)
+           attributes & FILE_ATTRIBUTE_DIRECTORY) {
                goto ignore;
+       }
 #endif
 
+       struct stat st;
        if (x_stat(path, &st) != 0) {
                goto failure;
        }
        if (S_ISDIR(st.st_mode)) {
-               /* Ignore directory, typically $PWD. */
+               // Ignore directory, typically $PWD.
                goto ignore;
        }
        if (!S_ISREG(st.st_mode)) {
-               /* Device, pipe, socket or other strange creature. */
+               // Device, pipe, socket or other strange creature.
                cc_log("Non-regular include file %s", path);
                goto failure;
        }
 
-       /* Let's hash the include file. */
+       // Canonicalize path for comparison; clang uses ./header.h.
+       char *canonical = path;
+       size_t canonical_len = path_len;
+       if (canonical[0] == '.' && canonical[1] == '/') {
+               canonical += 2;
+               canonical_len -= 2;
+       }
+
+       for (size_t i = 0; i < ignore_headers_len; i++) {
+               char *ignore = ignore_headers[i];
+               size_t ignore_len = strlen(ignore);
+               if (ignore_len > canonical_len) {
+                       continue;
+               }
+               if (strncmp(canonical, ignore, ignore_len) == 0
+                   && (ignore[ignore_len-1] == DIR_DELIM_CH
+                       || canonical[ignore_len] == DIR_DELIM_CH
+                       || canonical[ignore_len] == '\0')) {
+                       goto ignore;
+               }
+       }
+
+       // Let's hash the include file.
        if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_MTIME)
            && st.st_mtime >= time_of_compilation) {
                cc_log("Include file %s too new", path);
@@ -576,25 +570,25 @@ remember_include_file(char *path, struct mdfour *cpp_hash)
                goto failure;
        }
 
+       struct mdfour fhash;
        hash_start(&fhash);
 
-       is_pch = is_precompiled_header(path);
+       bool is_pch = is_precompiled_header(path);
        if (is_pch) {
-               struct file_hash pch_hash;
                if (!hash_file(&fhash, path)) {
                        goto failure;
                }
+               struct file_hash pch_hash;
                hash_result_as_bytes(&fhash, pch_hash.hash);
                pch_hash.size = fhash.totalN;
                hash_delimiter(cpp_hash, "pch_hash");
                hash_buffer(cpp_hash, pch_hash.hash, sizeof(pch_hash.hash));
        }
-       if (conf->direct_mode) {
-               struct file_hash *h;
-
-               if (!is_pch) { /* else: the file has already been hashed. */
-                       int result;
 
+       if (conf->direct_mode) {
+               if (!is_pch) { // else: the file has already been hashed.
+                       char *source = NULL;
+                       size_t size;
                        if (st.st_size > 0) {
                                if (!read_file(path, st.st_size, &source, &size)) {
                                        goto failure;
@@ -604,14 +598,15 @@ remember_include_file(char *path, struct mdfour *cpp_hash)
                                size = 0;
                        }
 
-                       result = hash_source_code_string(conf, &fhash, source, size, path);
+                       int result = hash_source_code_string(conf, &fhash, source, size, path);
+                       free(source);
                        if (result & HASH_SOURCE_CODE_ERROR
                            || result & HASH_SOURCE_CODE_FOUND_TIME) {
                                goto failure;
                        }
                }
 
-               h = x_malloc(sizeof(*h));
+               struct file_hash *h = x_malloc(sizeof(*h));
                hash_result_as_bytes(&fhash, h->hash);
                h->size = fhash.totalN;
                hashtable_insert(included_files, path, h);
@@ -619,7 +614,6 @@ remember_include_file(char *path, struct mdfour *cpp_hash)
                free(path);
        }
 
-       free(source);
        return;
 
 failure:
@@ -627,50 +621,51 @@ failure:
                cc_log("Disabling direct mode");
                conf->direct_mode = false;
        }
-       /* Fall through. */
+       // Fall through.
 ignore:
        free(path);
-       free(source);
 }
 
-/*
- * Make a relative path from current working directory to path if path is under
- * the base directory. Takes over ownership of path. Caller frees.
- */
+// Make a relative path from current working directory to path if path is under
+// the base directory. Takes over ownership of path. Caller frees.
 static char *
 make_relative_path(char *path)
 {
-       char *canon_path, *path_suffix = NULL;
-       struct stat st;
-
        if (str_eq(conf->base_dir, "") || !str_startswith(path, conf->base_dir)) {
                return path;
        }
 
-       /* x_realpath only works for existing paths, so if path doesn't exist, try
-        * dirname(path) and assemble the path afterwards. We only bother to try
-        * canonicalizing one of these two paths since a compiler path argument
-        * typically only makes sense if path or dirname(path) exists. */
+#ifdef _WIN32
+       if (path[0] == '/') {
+               path++;  // Skip leading slash.
+       }
+#endif
+
+       // x_realpath only works for existing paths, so if path doesn't exist, try
+       // dirname(path) and assemble the path afterwards. We only bother to try
+       // canonicalizing one of these two paths since a compiler path argument
+       // typically only makes sense if path or dirname(path) exists.
+       char *path_suffix = NULL;
+       struct stat st;
        if (stat(path, &st) != 0) {
-               /* path doesn't exist. */
-               char *dir, *p;
-               dir = dirname(path);
+               // path doesn't exist.
+               char *dir = dirname(path);
                if (stat(dir, &st) != 0) {
-                       /* And neither does its parent directory, so no action to take. */
+                       // And neither does its parent directory, so no action to take.
                        free(dir);
                        return path;
                }
+               free(dir);
                path_suffix = basename(path);
-               p = path;
+               char *p = path;
                path = dirname(path);
                free(p);
        }
 
-       canon_path = x_realpath(path);
+       char *canon_path = x_realpath(path);
        if (canon_path) {
-               char *relpath;
                free(path);
-               relpath = get_relative_path(get_current_working_dir(), canon_path);
+               char *relpath = get_relative_path(get_current_working_dir(), canon_path);
                free(canon_path);
                if (path_suffix) {
                        path = format("%s/%s", relpath, path_suffix);
@@ -681,80 +676,90 @@ make_relative_path(char *path)
                        return relpath;
                }
        } else {
-               /* path doesn't exist, so leave it as it is. */
+               // path doesn't exist, so leave it as it is.
                free(path_suffix);
                return path;
        }
 }
 
-/*
- * This function reads and hashes a file. While doing this, it also does these
- * things:
- *
- * - Makes include file paths for which the base directory is a prefix relative
- *   when computing the hash sum.
- * - Stores the paths and hashes of included files in the global variable
- *   included_files.
- */
+// This function reads and hashes a file. While doing this, it also does these
+// things:
+//
+// - Makes include file paths for which the base directory is a prefix relative
+//   when computing the hash sum.
+// - Stores the paths and hashes of included files in the global variable
+//   included_files.
 static bool
 process_preprocessed_file(struct mdfour *hash, const char *path)
 {
        char *data;
-       char *p, *q, *end;
        size_t size;
-
        if (!read_file(path, 0, &data, &size)) {
                return false;
        }
 
-       included_files = create_hashtable(1000, hash_from_string, strings_equal);
+       ignore_headers = NULL;
+       ignore_headers_len = 0;
+       if (!str_eq(conf->ignore_headers_in_manifest, "")) {
+               char *header, *p, *q, *saveptr = NULL;
+               p = x_strdup(conf->ignore_headers_in_manifest);
+               q = p;
+               while ((header = strtok_r(q, PATH_DELIM, &saveptr))) {
+                       ignore_headers = x_realloc(ignore_headers,
+                                                  (ignore_headers_len+1) * sizeof(char *));
+                       ignore_headers[ignore_headers_len++] = x_strdup(header);
+                       q = NULL;
+               }
+               free(p);
+       }
 
-       /* Bytes between p and q are pending to be hashed. */
-       end = data + size;
-       p = data;
-       q = data;
-       /* There must be at least 7 characters (# 1 "x") left to potentially find an
-        * include file path. */
+       if (!included_files) {
+               included_files = create_hashtable(1000, hash_from_string, strings_equal);
+       }
+
+       // Bytes between p and q are pending to be hashed.
+       char *p = data;
+       char *q = data;
+       char *end = data + size;
+
+       // There must be at least 7 characters (# 1 "x") left to potentially find an
+       // include file path.
        while (q < end - 7) {
-               /*
-                * Check if we look at a line containing the file name of an included file.
-                * At least the following formats exist (where N is a positive integer):
-                *
-                * GCC:
-                *
-                *   # N "file"
-                *   # N "file" N
-                *   #pragma GCC pch_preprocess "file"
-                *
-                * HP's compiler:
-                *
-                *   #line N "file"
-                *
-                * AIX's compiler:
-                *
-                *   #line N "file"
-                *   #line N
-                *
-                * Note that there may be other lines starting with '#' left after
-                * preprocessing as well, for instance "#    pragma".
-                */
+               // Check if we look at a line containing the file name of an included file.
+               // At least the following formats exist (where N is a positive integer):
+               //
+               // GCC:
+               //
+               //   # N "file"
+               //   # N "file" N
+               //   #pragma GCC pch_preprocess "file"
+               //
+               // HP's compiler:
+               //
+               //   #line N "file"
+               //
+               // AIX's compiler:
+               //
+               //   #line N "file"
+               //   #line N
+               //
+               // Note that there may be other lines starting with '#' left after
+               // preprocessing as well, for instance "#    pragma".
                if (q[0] == '#'
-                   /* GCC: */
+                   // GCC:
                    && ((q[1] == ' ' && q[2] >= '0' && q[2] <= '9')
-                       /* GCC precompiled header: */
+                       // GCC precompiled header:
                        || (q[1] == 'p'
                            && str_startswith(&q[2], "ragma GCC pch_preprocess "))
-                       /* HP/AIX: */
+                       // HP/AIX:
                        || (q[1] == 'l' && q[2] == 'i' && q[3] == 'n' && q[4] == 'e'
                            && q[5] == ' '))
                    && (q == data || q[-1] == '\n')) {
-                       char *path;
-
-                       /* Workarounds for preprocessor linemarker bugs in GCC version 6 */
+                       // Workarounds for preprocessor linemarker bugs in GCC version 6.
                        if (q[2] == '3') {
                                if (str_startswith(q, "# 31 \"<command-line>\"\n")) {
-                                       /* Bogus extra line with #31, after the regular #1:
-                                          Ignore the whole line, and continue parsing */
+                                       // Bogus extra line with #31, after the regular #1: Ignore the whole
+                                       // line, and continue parsing.
                                        hash_buffer(hash, p, q - p);
                                        while (q < end && *q != '\n') {
                                                q++;
@@ -763,8 +768,8 @@ process_preprocessed_file(struct mdfour *hash, const char *path)
                                        p = q;
                                        continue;
                                } else if (str_startswith(q, "# 32 \"<command-line>\" 2\n")) {
-                                       /* Bogus wrong line with #32, instead of regular #1:
-                                          Replace the line number with the usual one */
+                                       // Bogus wrong line with #32, instead of regular #1: Replace the line
+                                       // number with the usual one.
                                        hash_buffer(hash, p, q - p);
                                        q += 1;
                                        q[0] = '#';
@@ -778,7 +783,7 @@ process_preprocessed_file(struct mdfour *hash, const char *path)
                                q++;
                        }
                        if (q < end && *q == '\n') {
-                               /* A newline before the quotation mark -> no match. */
+                               // A newline before the quotation mark -> no match.
                                continue;
                        }
                        q++;
@@ -787,17 +792,28 @@ process_preprocessed_file(struct mdfour *hash, const char *path)
                                free(data);
                                return false;
                        }
-                       /* q points to the beginning of an include file path */
+                       // q points to the beginning of an include file path
                        hash_buffer(hash, p, q - p);
                        p = q;
                        while (q < end && *q != '"') {
                                q++;
                        }
-                       /* p and q span the include file path */
-                       path = x_strndup(p, q - p);
-                       path = make_relative_path(path);
-                       hash_string(hash, path);
-                       remember_include_file(path, hash);
+                       // Look for preprocessor flags, after the "filename".
+                       bool system = false;
+                       char *r = q + 1;
+                       while (r < end && *r != '\n') {
+                               if (*r == '3') { // System header.
+                                       system = true;
+                               }
+                               r++;
+                       }
+                       // p and q span the include file path.
+                       char *inc_path = x_strndup(p, q - p);
+                       if (!has_absolute_include_headers) {
+                               has_absolute_include_headers = is_absolute_path(inc_path);
+                       }
+                       inc_path = make_relative_path(inc_path);
+                       remember_include_file(inc_path, hash, system);
                        p = q;
                } else {
                        q++;
@@ -807,46 +823,128 @@ process_preprocessed_file(struct mdfour *hash, const char *path)
        hash_buffer(hash, p, (end - p));
        free(data);
 
-       /* Explicitly check the .gch/.pch/.pth file, Clang does not include any
-        * mention of it in the preprocessed output. */
+       // Explicitly check the .gch/.pch/.pth file, Clang does not include any
+       // mention of it in the preprocessed output.
        if (included_pch_file) {
                char *path = x_strdup(included_pch_file);
                path = make_relative_path(path);
                hash_string(hash, path);
-               remember_include_file(path, hash);
+               remember_include_file(path, hash, false);
        }
 
        return true;
 }
 
-/* Copy or link a file to the cache. */
+// Replace absolute paths with relative paths in the provided dependency file.
 static void
-put_file_in_cache(const char *source, const char *dest)
+use_relative_paths_in_depfile(const char *depfile)
 {
-       int ret;
-       struct stat st;
-       bool do_link = conf->hard_link && !conf->compression;
+       if (str_eq(conf->base_dir, "")) {
+               cc_log("Base dir not set, skip using relative paths");
+               return; // nothing to do
+       }
+       if (!has_absolute_include_headers) {
+               cc_log("No absolute path for included files found, skip using relative"
+                      " paths");
+               return; // nothing to do
+       }
+
+       FILE *f;
+       f = fopen(depfile, "r");
+       if (!f) {
+               cc_log("Cannot open dependency file: %s (%s)", depfile, strerror(errno));
+               return;
+       }
+
+       char *tmp_file = format("%s.tmp", depfile);
+       FILE *tmpf = create_tmp_file(&tmp_file, "w");
+
+       bool result = false;
+       char buf[10000];
+       while (fgets(buf, sizeof(buf), f) && !ferror(tmpf)) {
+               char *saveptr;
+               char *token = strtok_r(buf, " \t", &saveptr);
+               while (token) {
+                       char *relpath;
+                       if (is_absolute_path(token) && str_startswith(token, conf->base_dir)) {
+                               relpath = make_relative_path(x_strdup(token));
+                               result = true;
+                       } else {
+                               relpath = token;
+                       }
+                       if (token != buf) { // This is a dependency file.
+                               fputc(' ', tmpf);
+                       }
+                       fputs(relpath, tmpf);
+                       if (relpath != token) {
+                               free(relpath);
+                       }
+                       token = strtok_r(NULL, " \t", &saveptr);
+               }
+       }
+
+       if (ferror(f)) {
+               cc_log("Error reading dependency file: %s, skip relative path usage",
+                      depfile);
+               result = false;
+               goto out;
+       }
+       if (ferror(tmpf)) {
+               cc_log("Error writing temporary dependency file: %s, skip relative path"
+                      " usage", tmp_file);
+               result = false;
+               goto out;
+       }
+
+out:
+       fclose(tmpf);
+       fclose(f);
+       if (result) {
+               if (x_rename(tmp_file, depfile) != 0) {
+                       cc_log("Error renaming dependency file: %s -> %s (%s), skip relative"
+                              " path usage", tmp_file, depfile, strerror(errno));
+                       result = false;
+               } else {
+                       cc_log("Renamed dependency file: %s -> %s", tmp_file, depfile);
+               }
+       }
+       if (!result) {
+               cc_log("Removing temporary dependency file: %s", tmp_file);
+               x_unlink(tmp_file);
+       }
+       free(tmp_file);
+}
 
+// Copy or link a file to the cache.
+static void
+put_file_in_cache(const char *source, const char *dest)
+{
        assert(!conf->read_only);
        assert(!conf->read_only_direct);
 
+       bool do_link = conf->hard_link && !conf->compression;
        if (do_link) {
                x_unlink(dest);
-               ret = link(source, dest);
-       } else {
-               ret = copy_file(
-                 source, dest, conf->compression ? conf->compression_level : 0);
+               int ret = link(source, dest);
+               if (ret != 0) {
+                       cc_log("Failed to link %s to %s: %s", source, dest, strerror(errno));
+                       cc_log("Falling back to copying");
+                       do_link = false;
+               }
        }
-       if (ret != 0) {
-               cc_log("Failed to %s %s to %s: %s",
-                      do_link ? "link" : "copy",
-                      source,
-                      dest,
-                      strerror(errno));
-               stats_update(STATS_ERROR);
-               failed();
+       if (!do_link) {
+               int ret = copy_file(
+                 source, dest, conf->compression ? conf->compression_level : 0);
+               if (ret != 0) {
+                       cc_log("Failed to copy %s to %s: %s", source, dest, strerror(errno));
+                       stats_update(STATS_ERROR);
+                       failed();
+               }
        }
+
        cc_log("Stored in cache: %s -> %s", source, dest);
+
+       struct stat st;
        if (x_stat(dest, &st) != 0) {
                stats_update(STATS_ERROR);
                failed();
@@ -854,13 +952,12 @@ put_file_in_cache(const char *source, const char *dest)
        stats_update_size(file_size(&st), 1);
 }
 
-/* Copy or link a file from the cache. */
+// Copy or link a file from the cache.
 static void
 get_file_from_cache(const char *source, const char *dest)
 {
        int ret;
        bool do_link = conf->hard_link && !file_is_compressed(source);
-
        if (do_link) {
                x_unlink(dest);
                ret = link(source, dest);
@@ -870,7 +967,7 @@ get_file_from_cache(const char *source, const char *dest)
 
        if (ret == -1) {
                if (errno == ENOENT || errno == ESTALE) {
-                       /* Someone removed the file just before we began copying? */
+                       // Someone removed the file just before we began copying?
                        cc_log("Cache file %s just disappeared from cache", source);
                        stats_update(STATS_MISSING);
                } else {
@@ -882,8 +979,8 @@ get_file_from_cache(const char *source, const char *dest)
                        stats_update(STATS_ERROR);
                }
 
-               /* If there was trouble getting a file from the cached result, wipe the
-                * whole cached result for consistency. */
+               // If there was trouble getting a file from the cached result, wipe the
+               // whole cached result for consistency.
                x_unlink(cached_stderr);
                x_unlink(cached_obj);
                x_unlink(cached_dep);
@@ -895,7 +992,7 @@ get_file_from_cache(const char *source, const char *dest)
        cc_log("Created from cache: %s -> %s", source, dest);
 }
 
-/* Send cached stderr, if any, to stderr. */
+// Send cached stderr, if any, to stderr.
 static void
 send_cached_stderr(void)
 {
@@ -906,12 +1003,9 @@ send_cached_stderr(void)
        }
 }
 
-/* Create or update the manifest file. */
+// Create or update the manifest file.
 void update_manifest_file(void)
 {
-       struct stat st;
-       size_t old_size = 0; /* in bytes */
-
        if (!conf->direct_mode
            || !included_files
            || conf->read_only
@@ -919,6 +1013,8 @@ void update_manifest_file(void)
                return;
        }
 
+       struct stat st;
+       size_t old_size = 0; // in bytes
        if (stat(manifest_path, &st) == 0) {
                old_size = file_size(&st);
        }
@@ -933,23 +1029,19 @@ void update_manifest_file(void)
        }
 }
 
-/* run the real compiler and put the result in cache */
+// Run the real compiler and put the result in cache.
 static void
 to_cache(struct args *args)
 {
-       char *tmp_stdout, *tmp_stderr, *tmp_cov;
-       char *tmp_dwo = NULL;
-       struct stat st;
-       int status, tmp_stdout_fd, tmp_stderr_fd;
-
-       tmp_stdout = format("%s.tmp.stdout", cached_obj);
-       tmp_stdout_fd = create_tmp_fd(&tmp_stdout);
-       tmp_stderr = format("%s.tmp.stderr", cached_obj);
-       tmp_stderr_fd = create_tmp_fd(&tmp_stderr);
+       char *tmp_stdout = format("%s.tmp.stdout", cached_obj);
+       int tmp_stdout_fd = create_tmp_fd(&tmp_stdout);
+       char *tmp_stderr = format("%s.tmp.stderr", cached_obj);
+       int tmp_stderr_fd = create_tmp_fd(&tmp_stderr);
 
+       char *tmp_cov;
        if (generating_coverage) {
                char *tmp_aux;
-               /* gcc has some funny rule about max extension length */
+               // GCC has some funny rule about max extension length.
                if (strlen(get_extension(output_obj)) < 6) {
                        tmp_aux = remove_extension(output_obj);
                } else {
@@ -961,10 +1053,10 @@ to_cache(struct args *args)
                tmp_cov = NULL;
        }
 
-       /* GCC (at least 4.8 and 4.9) forms the .dwo file name by removing everything
-        * after (and including) the last "." from the object file name and then
-        * appending ".dwo".
-        */
+       // GCC (at least 4.8 and 4.9) forms the .dwo file name by removing everything
+       // after (and including) the last "." from the object file name and then
+       // appending ".dwo".
+       char *tmp_dwo = NULL;
        if (using_split_dwarf) {
                char *base_name = remove_extension(output_obj);
                tmp_dwo = format("%s.dwo", base_name);
@@ -979,11 +1071,10 @@ to_cache(struct args *args)
                args_add(args, output_dia);
        }
 
-       /* Turn off DEPENDENCIES_OUTPUT when running cc1, because
-        * otherwise it will emit a line like
-        *
-        *  tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i
-        */
+       // Turn off DEPENDENCIES_OUTPUT when running cc1, because otherwise it will
+       // emit a line like this:
+       //
+       //   tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i
        x_unsetenv("DEPENDENCIES_OUTPUT");
 
        if (conf->run_second_cpp) {
@@ -993,11 +1084,13 @@ to_cache(struct args *args)
        }
 
        cc_log("Running real compiler");
-       status = execute(args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid);
+       int status =
+         execute(args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid);
        args_pop(args, 3);
 
+       struct stat st;
        if (x_stat(tmp_stdout, &st) != 0) {
-               /* The stdout file was removed - cleanup in progress? Better bail out. */
+               // The stdout file was removed - cleanup in progress? Better bail out.
                stats_update(STATS_MISSING);
                tmp_unlink(tmp_stdout);
                tmp_unlink(tmp_stderr);
@@ -1020,37 +1113,35 @@ to_cache(struct args *args)
        }
        tmp_unlink(tmp_stdout);
 
-       /*
-        * Merge stderr from the preprocessor (if any) and stderr from the real
-        * compiler into tmp_stderr.
-        */
+       // Merge stderr from the preprocessor (if any) and stderr from the real
+       // compiler into tmp_stderr.
        if (cpp_stderr) {
-               int fd_cpp_stderr;
-               int fd_real_stderr;
-               int fd_result;
-               char *tmp_stderr2;
-
-               tmp_stderr2 = format("%s.2", tmp_stderr);
+               char *tmp_stderr2 = format("%s.2", tmp_stderr);
                if (x_rename(tmp_stderr, tmp_stderr2)) {
                        cc_log("Failed to rename %s to %s: %s", tmp_stderr, tmp_stderr2,
                               strerror(errno));
                        failed();
                }
-               fd_cpp_stderr = open(cpp_stderr, O_RDONLY | O_BINARY);
+
+               int fd_cpp_stderr = open(cpp_stderr, O_RDONLY | O_BINARY);
                if (fd_cpp_stderr == -1) {
                        cc_log("Failed opening %s: %s", cpp_stderr, strerror(errno));
                        failed();
                }
-               fd_real_stderr = open(tmp_stderr2, O_RDONLY | O_BINARY);
+
+               int fd_real_stderr = open(tmp_stderr2, O_RDONLY | O_BINARY);
                if (fd_real_stderr == -1) {
                        cc_log("Failed opening %s: %s", tmp_stderr2, strerror(errno));
                        failed();
                }
-               fd_result = open(tmp_stderr, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
+
+               int fd_result =
+                 open(tmp_stderr, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
                if (fd_result == -1) {
                        cc_log("Failed opening %s: %s", tmp_stderr, strerror(errno));
                        failed();
                }
+
                copy_fd(fd_cpp_stderr, fd_result);
                copy_fd(fd_real_stderr, fd_result);
                close(fd_cpp_stderr);
@@ -1061,13 +1152,12 @@ to_cache(struct args *args)
        }
 
        if (status != 0) {
-               int fd;
                cc_log("Compiler gave exit status %d", status);
                stats_update(STATS_STATUS);
 
-               fd = open(tmp_stderr, O_RDONLY | O_BINARY);
+               int fd = open(tmp_stderr, O_RDONLY | O_BINARY);
                if (fd != -1) {
-                       /* We can output stderr immediately instead of rerunning the compiler. */
+                       // We can output stderr immediately instead of rerunning the compiler.
                        copy_fd(fd, 2);
                        close(fd);
                        tmp_unlink(tmp_stderr);
@@ -1123,20 +1213,20 @@ to_cache(struct args *args)
                }
                cc_log("Stored in cache: %s", cached_stderr);
                if (!conf->compression
-                   /* If the file was compressed, obtain the size again: */
+                   // If the file was compressed, obtain the size again:
                    || x_stat(cached_stderr, &st) == 0) {
                        stats_update_size(file_size(&st), 1);
                }
        } else {
                tmp_unlink(tmp_stderr);
                if (conf->recache) {
-                       /* If recaching, we need to remove any previous .stderr. */
+                       // If recaching, we need to remove any previous .stderr.
                        x_unlink(cached_stderr);
                }
        }
 
        if (generating_coverage) {
-               /* gcc won't generate notes if there is no code */
+               // GCC won't generate notes if there is no code.
                if (stat(tmp_cov, &st) != 0 && errno == ENOENT) {
                        FILE *f = fopen(cached_cov, "wb");
                        cc_log("Creating placeholder: %s", cached_cov);
@@ -1171,14 +1261,14 @@ to_cache(struct args *args)
        }
 
        if (generating_dependencies) {
+               use_relative_paths_in_depfile(output_dep);
                put_file_in_cache(output_dep, cached_dep);
        }
        stats_update(STATS_TOCACHE);
 
-       /* Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can
-        * be done almost anywhere, but we might as well do it near the end as we
-        * save the stat call if we exit early.
-        */
+       // Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can
+       // be done almost anywhere, but we might as well do it near the end as we
+       // save the stat call if we exit early.
        {
                char *first_level_dir = dirname(stats_file);
                if (create_cachedirtag(first_level_dir) != 0) {
@@ -1189,8 +1279,8 @@ to_cache(struct args *args)
                }
                free(first_level_dir);
 
-               /* Remove any CACHEDIR.TAG on the cache_dir level where it was located in
-                * previous ccache versions. */
+               // Remove any CACHEDIR.TAG on the cache_dir level where it was located in
+               // previous ccache versions.
                if (getpid() % 1000 == 0) {
                        char *path = format("%s/CACHEDIR.TAG", conf->cache_dir);
                        x_unlink(path);
@@ -1198,7 +1288,7 @@ to_cache(struct args *args)
                }
        }
 
-       /* Everything OK. */
+       // Everything OK.
        send_cached_stderr();
        update_manifest_file();
 
@@ -1208,55 +1298,53 @@ to_cache(struct args *args)
        free(tmp_dwo);
 }
 
-/*
- * Find the object file name by running the compiler in preprocessor mode.
- * Returns the hash as a heap-allocated hex string.
- */
+// Find the object file name by running the compiler in preprocessor mode.
+// Returns the hash as a heap-allocated hex string.
 static struct file_hash *
 get_object_name_from_cpp(struct args *args, struct mdfour *hash)
 {
-       char *input_base;
-       char *tmp;
-       char *path_stdout, *path_stderr;
-       int status, path_stderr_fd;
-       struct file_hash *result;
-
-       /* ~/hello.c -> tmp.hello.123.i
-        * limit the basename to 10
-        * characters in order to cope with filesystem with small
-        * maximum filename length limits */
-       input_base = basename(input_file);
-       tmp = strchr(input_base, '.');
-       if (tmp) {
-               *tmp = 0;
-       }
-       if (strlen(input_base) > 10) {
-               input_base[10] = 0;
-       }
-
-       path_stderr = format("%s/tmp.cpp_stderr", temp_dir());
-       path_stderr_fd = create_tmp_fd(&path_stderr);
-       add_pending_tmp_file(path_stderr);
-
        time_of_compilation = time(NULL);
 
+       char *path_stderr = format("%s/tmp.cpp_stderr", temp_dir());
+       int path_stderr_fd = create_tmp_fd(&path_stderr);
+       add_pending_tmp_file(path_stderr);
+
+       char *path_stdout;
+       int status;
        if (direct_i_file) {
-               /* We are compiling a .i or .ii file - that means we can skip the cpp stage
-                * and directly form the correct i_tmpfile. */
+               // We are compiling a .i or .ii file - that means we can skip the cpp stage
+               // and directly form the correct i_tmpfile.
                path_stdout = input_file;
                status = 0;
        } else {
-               /* Run cpp on the input file to obtain the .i. */
-               int path_stdout_fd;
+               // Run cpp on the input file to obtain the .i.
+
+               // Limit the basename to 10 characters in order to cope with filesystem with
+               // small maximum filename length limits.
+               char *input_base = basename(input_file);
+               char *tmp = strchr(input_base, '.');
+               if (tmp) {
+                       *tmp = 0;
+               }
+               if (strlen(input_base) > 10) {
+                       input_base[10] = 0;
+               }
+
                path_stdout = format("%s/%s.stdout", temp_dir(), input_base);
-               path_stdout_fd = create_tmp_fd(&path_stdout);
+               int path_stdout_fd = create_tmp_fd(&path_stdout);
                add_pending_tmp_file(path_stdout);
 
+               int args_added = 2;
                args_add(args, "-E");
+               if (conf->keep_comments_cpp) {
+                       args_add(args, "-C");
+                       args_added = 3;
+               }
                args_add(args, input_file);
+               add_prefix(args, conf->prefix_command_cpp);
                cc_log("Running preprocessor");
                status = execute(args->argv, path_stdout_fd, path_stderr_fd, &compiler_pid);
-               args_pop(args, 2);
+               args_pop(args, args_added);
        }
 
        if (status != 0) {
@@ -1266,8 +1354,8 @@ get_object_name_from_cpp(struct args *args, struct mdfour *hash)
        }
 
        if (conf->unify) {
-               /* When we are doing the unifying tricks we need to include the input file
-                * name in the hash to get the warnings right. */
+               // When we are doing the unifying tricks we need to include the input file
+               // name in the hash to get the warnings right.
                hash_delimiter(hash, "unifyfilename");
                hash_string(hash, input_file);
 
@@ -1293,8 +1381,8 @@ get_object_name_from_cpp(struct args *args, struct mdfour *hash)
        if (direct_i_file) {
                i_tmpfile = input_file;
        } else {
-               /* i_tmpfile needs the proper cpp_extension for the compiler to do its
-                * thing correctly. */
+               // i_tmpfile needs the proper cpp_extension for the compiler to do its
+               // thing correctly
                i_tmpfile = format("%s.%s", path_stdout, conf->cpp_extension);
                x_rename(path_stdout, i_tmpfile);
                add_pending_tmp_file(i_tmpfile);
@@ -1303,17 +1391,14 @@ get_object_name_from_cpp(struct args *args, struct mdfour *hash)
        if (conf->run_second_cpp) {
                free(path_stderr);
        } else {
-               /*
-                * If we are using the CPP trick, we need to remember this
-                * stderr data and output it just before the main stderr from
-                * the compiler pass.
-                */
+               // If we are using the CPP trick, we need to remember this stderr data and
+               // output it just before the main stderr from the compiler pass.
                cpp_stderr = path_stderr;
                hash_delimiter(hash, "runsecondcpp");
                hash_string(hash, "false");
        }
 
-       result = x_malloc(sizeof(*result));
+       struct file_hash *result = x_malloc(sizeof(*result));
        hash_result_as_bytes(hash, result->hash);
        result->size = hash->totalN;
        return result;
@@ -1322,8 +1407,7 @@ get_object_name_from_cpp(struct args *args, struct mdfour *hash)
 static void
 update_cached_result_globals(struct file_hash *hash)
 {
-       char *object_name;
-       object_name = format_hash_as_string(hash->hash, hash->size);
+       char *object_name = format_hash_as_string(hash->hash, hash->size);
        cached_obj_hash = hash;
        cached_obj = get_path_in_cache(object_name, ".o");
        cached_stderr = get_path_in_cache(object_name, ".stderr");
@@ -1341,16 +1425,14 @@ update_cached_result_globals(struct file_hash *hash)
        free(object_name);
 }
 
-/*
- * Hash mtime or content of a file, or the output of a command, according to
- * the CCACHE_COMPILERCHECK setting.
- */
+// Hash mtime or content of a file, or the output of a command, according to
+// the CCACHE_COMPILERCHECK setting.
 static void
 hash_compiler(struct mdfour *hash, struct stat *st, const char *path,
               bool allow_command)
 {
        if (str_eq(conf->compiler_check, "none")) {
-               /* Do nothing. */
+               // Do nothing.
        } else if (str_eq(conf->compiler_check, "mtime")) {
                hash_delimiter(hash, "cc_mtime");
                hash_int(hash, st->st_size);
@@ -1361,7 +1443,7 @@ hash_compiler(struct mdfour *hash, struct stat *st, const char *path,
        } else if (str_eq(conf->compiler_check, "content") || !allow_command) {
                hash_delimiter(hash, "cc_content");
                hash_file(hash, path);
-       } else { /* command string */
+       } else { // command string
                if (!hash_multicommand_output(
                      hash, conf->compiler_check, orig_args->argv[0])) {
                        fatal("Failure running compiler check command: %s", conf->compiler_check);
@@ -1369,84 +1451,84 @@ hash_compiler(struct mdfour *hash, struct stat *st, const char *path,
        }
 }
 
-/*
- * Note that these compiler checks are unreliable, so nothing should
- * hard-depend on them.
- */
+// Note that these compiler checks are unreliable, so nothing should
+// hard-depend on them.
 
 static bool
 compiler_is_clang(struct args *args)
 {
        char *name = basename(args->argv[0]);
-       bool is = strstr(name, "clang") != NULL;
+       bool result = strstr(name, "clang") != NULL;
        free(name);
-       return is;
+       return result;
 }
 
 static bool
 compiler_is_gcc(struct args *args)
 {
        char *name = basename(args->argv[0]);
-       bool is = strstr(name, "gcc") || strstr(name, "g++");
+       bool result = strstr(name, "gcc") || strstr(name, "g++");
        free(name);
-       return is;
+       return result;
 }
 
-/*
- * Update a hash sum with information common for the direct and preprocessor
- * modes.
- */
+// Update a hash sum with information common for the direct and preprocessor
+// modes.
 static void
 calculate_common_hash(struct args *args, struct mdfour *hash)
 {
-       struct stat st;
-       char *p;
-       const char *full_path;
-#ifdef _WIN32
-       const char *ext;
-       char full_path_win_ext[MAX_PATH + 1] = {0};
-#endif
-
        hash_string(hash, HASH_PREFIX);
 
-       /*
-        * We have to hash the extension, as a .i file isn't treated the same
-        * by the compiler as a .ii file.
-        */
+       // We have to hash the extension, as a .i file isn't treated the same by the
+       // compiler as a .ii file.
        hash_delimiter(hash, "ext");
        hash_string(hash, conf->cpp_extension);
 
 #ifdef _WIN32
-       ext = strrchr(args->argv[0], '.');
+       const char *ext = strrchr(args->argv[0], '.');
+       char full_path_win_ext[MAX_PATH + 1] = {0};
        add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext,
                                      args->argv[0]);
-       full_path = full_path_win_ext;
+       const char *full_path = full_path_win_ext;
 #else
-       full_path = args->argv[0];
+       const char *full_path = args->argv[0];
 #endif
 
+       struct stat st;
        if (x_stat(full_path, &st) != 0) {
                stats_update(STATS_COMPILER);
                failed();
        }
 
-       /*
-        * Hash information about the compiler.
-        */
+       // Hash information about the compiler.
        hash_compiler(hash, &st, args->argv[0], true);
 
-       /*
-        * Also hash the compiler name as some compilers use hard links and
-        * behave differently depending on the real name.
-        */
+       // Also hash the compiler name as some compilers use hard links and behave
+       // differently depending on the real name.
        hash_delimiter(hash, "cc_name");
-       p = basename(args->argv[0]);
+       char *p = basename(args->argv[0]);
        hash_string(hash, p);
        free(p);
 
-       /* Possibly hash the current working directory. */
-       if (conf->hash_dir) {
+       // Possibly hash the current working directory.
+       if (generating_debuginfo && conf->hash_dir) {
                char *cwd = gnu_getcwd();
+               if (debug_prefix_map) {
+                       char *map = debug_prefix_map;
+                       char *sep = strchr(map, '=');
+                       if (sep) {
+                               char *old = x_strndup(map, sep - map);
+                               char *new = x_strdup(sep + 1);
+                               cc_log("Relocating debuginfo cwd %s, from %s to %s", cwd, old, new);
+                               if (str_startswith(cwd, old)) {
+                                       char *dir = format("%s%s", new, cwd + strlen(old));
+                                       free(cwd);
+                                       cwd = dir;
+                               }
+                               free(old);
+                               free(new);
+                       }
+               }
                if (cwd) {
                        hash_delimiter(hash, "cwd");
                        hash_string(hash, cwd);
@@ -1454,7 +1536,7 @@ calculate_common_hash(struct args *args, struct mdfour *hash)
                }
        }
 
-       /* Possibly hash the coverage data file path. */
+       // Possibly hash the coverage data file path.
        if (generating_coverage && profile_arcs) {
                char *dir = dirname(output_obj);
                if (profile_dir) {
@@ -1465,11 +1547,10 @@ calculate_common_hash(struct args *args, struct mdfour *hash)
                        dir = real_dir;
                }
                if (dir) {
-                       char *gcda_path;
                        char *base_name = basename(output_obj);
                        p = remove_extension(base_name);
                        free(base_name);
-                       gcda_path = format("%s/%s.gcda", dir, p);
+                       char *gcda_path = format("%s/%s.gcda", dir, p);
                        cc_log("Hashing coverage path %s", gcda_path);
                        free(p);
                        hash_delimiter(hash, "gcda");
@@ -1479,9 +1560,10 @@ calculate_common_hash(struct args *args, struct mdfour *hash)
        }
 
        if (!str_eq(conf->extra_files_to_hash, "")) {
-               char *path, *p, *q, *saveptr = NULL;
-               p = x_strdup(conf->extra_files_to_hash);
-               q = p;
+               char *p = x_strdup(conf->extra_files_to_hash);
+               char *q = p;
+               char *path;
+               char *saveptr = NULL;
                while ((path = strtok_r(q, PATH_DELIM, &saveptr))) {
                        cc_log("Hashing extra file %s", path);
                        hash_delimiter(hash, "extrafile");
@@ -1494,7 +1576,7 @@ calculate_common_hash(struct args *args, struct mdfour *hash)
                free(p);
        }
 
-       /* Possibly hash GCC_COLORS (for color diagnostics). */
+       // Possibly hash GCC_COLORS (for color diagnostics).
        if (compiler_is_gcc(args)) {
                const char *gcc_colors = getenv("GCC_COLORS");
                if (gcc_colors) {
@@ -1504,27 +1586,20 @@ calculate_common_hash(struct args *args, struct mdfour *hash)
        }
 }
 
-/*
- * Update a hash sum with information specific to the direct and preprocessor
- * modes and calculate the object hash. Returns the object hash on success,
- * otherwise NULL. Caller frees.
- */
+// Update a hash sum with information specific to the direct and preprocessor
+// modes and calculate the object hash. Returns the object hash on success,
+// otherwise NULL. Caller frees.
 static struct file_hash *
 calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
 {
-       int i;
-       struct stat st;
-       struct file_hash *object_hash = NULL;
-       char *p;
-
        if (direct_mode) {
                hash_delimiter(hash, "manifest version");
                hash_int(hash, MANIFEST_VERSION);
        }
 
-       /* first the arguments */
-       for (i = 1; i < args->argc; i++) {
-               /* -L doesn't affect compilation. */
+       // First the arguments.
+       for (int i = 1; i < args->argc; i++) {
+               // -L doesn't affect compilation.
                if (i < args->argc-1 && str_eq(args->argv[i], "-L")) {
                        i++;
                        continue;
@@ -1533,22 +1608,22 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                        continue;
                }
 
-               /* -Wl,... doesn't affect compilation. */
+               // -Wl,... doesn't affect compilation.
                if (str_startswith(args->argv[i], "-Wl,")) {
                        continue;
                }
 
-               /* The -fdebug-prefix-map option may be used in combination with
-                * CCACHE_BASEDIR to reuse results across different directories. Skip it
-                * from hashing. */
+               // The -fdebug-prefix-map option may be used in combination with
+               // CCACHE_BASEDIR to reuse results across different directories. Skip it
+               // from hashing.
                if (str_startswith(args->argv[i], "-fdebug-prefix-map=")) {
                        continue;
                }
 
-               /* When using the preprocessor, some arguments don't contribute
-                * to the hash. The theory is that these arguments will change
-                * the output of -E if they are going to have any effect at
-                * all. For precompiled headers this might not be the case. */
+               // When using the preprocessor, some arguments don't contribute to the
+               // hash. The theory is that these arguments will change the output of -E if
+               // they are going to have any effect at all. For precompiled headers this
+               // might not be the case.
                if (!direct_mode && !output_is_precompiled_header
                    && !using_precompiled_header) {
                        if (compopt_affects_cpp(args->argv[i])) {
@@ -1560,10 +1635,8 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                        }
                }
 
-               /* If we're generating dependencies, we make sure to skip the
-                * filename of the dependency file, since it doesn't impact the
-                * output.
-                */
+               // If we're generating dependencies, we make sure to skip the filename of
+               // the dependency file, since it doesn't impact the output.
                if (generating_dependencies) {
                        if (str_startswith(args->argv[i], "-Wp,")) {
                                if (str_startswith(args->argv[i], "-Wp,-MD,")
@@ -1576,29 +1649,29 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                                        continue;
                                }
                        } else if (str_startswith(args->argv[i], "-MF")) {
-                               bool separate_argument = (strlen(args->argv[i]) == 3);
-
-                               /* In either case, hash the "-MF" part. */
+                               // In either case, hash the "-MF" part.
                                hash_string_length(hash, args->argv[i], 3);
 
+                               bool separate_argument = (strlen(args->argv[i]) == 3);
                                if (separate_argument) {
-                                       /* Next argument is dependency name, so
-                                        * skip it. */
+                                       // Next argument is dependency name, so skip it.
                                        i++;
                                }
                                continue;
                        }
                }
 
-               p = NULL;
+               char *p = NULL;
                if (str_startswith(args->argv[i], "-specs=")) {
                        p = args->argv[i] + 7;
                } else if (str_startswith(args->argv[i], "--specs=")) {
                        p = args->argv[i] + 8;
                }
+
+               struct stat st;
                if (p && x_stat(p, &st) == 0) {
-                       /* If given an explicit specs file, then hash that file,
-                        * but don't include the path to it in the hash. */
+                       // If given an explicit specs file, then hash that file, but don't
+                       // include the path to it in the hash.
                        hash_delimiter(hash, "specs");
                        hash_compiler(hash, &st, p, false);
                        continue;
@@ -1622,7 +1695,7 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                        continue;
                }
 
-               /* All other arguments are included in the hash. */
+               // All other arguments are included in the hash.
                hash_delimiter(hash, "arg");
                hash_string(hash, args->argv[i]);
                if (i + 1 < args->argc && compopt_takes_arg(args->argv[i])) {
@@ -1632,25 +1705,17 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                }
        }
 
-       /*
-        * For profile generation (-fprofile-arcs, -fprofile-generate):
-        * - hash profile directory
-        * - output to the real file first
-        *
-        * For profile usage (-fprofile-use):
-        * - hash profile data
-        *
-        * -fbranch-probabilities and -fvpt usage is covered by
-        * -fprofile-generate/-fprofile-use.
-        *
-        * The profile directory can be specified as an argument to
-        * -fprofile-generate=, -fprofile-use=, or -fprofile-dir=.
-        */
-
-       /*
-        * We need to output to the real object first here, otherwise runtime
-        * artifacts will be produced in the wrong place.
-        */
+       // For profile generation (-fprofile-arcs, -fprofile-generate):
+       // - hash profile directory
+       //
+       // For profile usage (-fprofile-use):
+       // - hash profile data
+       //
+       // -fbranch-probabilities and -fvpt usage is covered by
+       // -fprofile-generate/-fprofile-use.
+       //
+       // The profile directory can be specified as an argument to
+       // -fprofile-generate=, -fprofile-use= or -fprofile-dir=.
        if (profile_generate) {
                if (!profile_dir) {
                        profile_dir = get_cwd();
@@ -1661,37 +1726,32 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
        }
 
        if (profile_use) {
-               /* Calculate gcda name */
-               char *gcda_name;
-               char *base_name;
-               base_name = remove_extension(output_obj);
+               // Calculate gcda name.
                if (!profile_dir) {
                        profile_dir = get_cwd();
                }
-               gcda_name = format("%s/%s.gcda", profile_dir, base_name);
+               char *base_name = remove_extension(output_obj);
+               char *gcda_name = format("%s/%s.gcda", profile_dir, base_name);
                cc_log("Adding profile data %s to our hash", gcda_name);
-               /* Add the gcda to our hash */
+               // Add the gcda to our hash.
                hash_delimiter(hash, "-fprofile-use");
                hash_file(hash, gcda_name);
                free(base_name);
                free(gcda_name);
        }
 
+       struct file_hash *object_hash = NULL;
        if (direct_mode) {
-               char *manifest_name;
-               int result;
-
-               /* Hash environment variables that affect the preprocessor output. */
-               const char **p;
+               // Hash environment variables that affect the preprocessor output.
                const char *envvars[] = {
                        "CPATH",
                        "C_INCLUDE_PATH",
                        "CPLUS_INCLUDE_PATH",
                        "OBJC_INCLUDE_PATH",
-                       "OBJCPLUS_INCLUDE_PATH", /* clang */
+                       "OBJCPLUS_INCLUDE_PATH", // clang
                        NULL
                };
-               for (p = envvars; *p; ++p) {
+               for (const char **p = envvars; *p; ++p) {
                        char *v = getenv(*p);
                        if (v) {
                                hash_delimiter(hash, *p);
@@ -1700,17 +1760,14 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                }
 
                if (!(conf->sloppiness & SLOPPY_FILE_MACRO)) {
-                       /*
-                        * The source code file or an include file may contain
-                        * __FILE__, so make sure that the hash is unique for
-                        * the file name.
-                        */
+                       // The source code file or an include file may contain __FILE__, so make
+                       // sure that the hash is unique for the file name.
                        hash_delimiter(hash, "inputfile");
                        hash_string(hash, input_file);
                }
 
                hash_delimiter(hash, "sourcecode");
-               result = hash_source_code_file(conf, hash, input_file);
+               int result = hash_source_code_file(conf, hash, input_file);
                if (result & HASH_SOURCE_CODE_ERROR) {
                        failed();
                }
@@ -1719,7 +1776,7 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                        conf->direct_mode = false;
                        return NULL;
                }
-               manifest_name = hash_result(hash);
+               char *manifest_name = hash_result(hash);
                manifest_path = get_path_in_cache(manifest_name, ".manifest");
                free(manifest_name);
                cc_log("Looking for object file hash in %s", manifest_path);
@@ -1730,8 +1787,23 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
                        cc_log("Did not find object file hash in manifest");
                }
        } else {
-               object_hash = get_object_name_from_cpp(args, hash);
-               cc_log("Got object file hash from preprocessor");
+               if (arch_args_size == 0) {
+                       object_hash = get_object_name_from_cpp(args, hash);
+                       cc_log("Got object file hash from preprocessor");
+               } else {
+                       args_add(args, "-arch");
+                       for (size_t i = 0; i < arch_args_size; ++i) {
+                               args_add(args, arch_args[i]);
+                               object_hash = get_object_name_from_cpp(args, hash);
+                               cc_log("Got object file hash from preprocessor with -arch %s",
+                                      arch_args[i]);
+                               if (i != arch_args_size - 1) {
+                                       free(object_hash);
+                               }
+                               args_pop(args, 1);
+                       }
+                       args_pop(args, 1);
+               }
                if (generating_dependencies) {
                        cc_log("Preprocessor created %s", output_dep);
                }
@@ -1740,36 +1812,30 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode)
        return object_hash;
 }
 
-/*
- * Try to return the compile result from cache. If we can return from cache
- * then this function exits with the correct status code, otherwise it returns.
- */
+// Try to return the compile result from cache. If we can return from cache
+// then this function exits with the correct status code, otherwise it returns.
 static void
 from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
 {
-       struct stat st;
-       bool produce_dep_file = false;
-
-       /* the user might be disabling cache hits */
+       // The user might be disabling cache hits.
        if (conf->recache) {
                return;
        }
 
+       struct stat st;
        if (stat(cached_obj, &st) != 0) {
                cc_log("Object file %s not in cache", cached_obj);
                return;
        }
 
-       /* Check if the diagnostic file is there. */
+       // Check if the diagnostic file is there.
        if (output_dia && stat(cached_dia, &st) != 0) {
                cc_log("Diagnostic file %s not in cache", cached_dia);
                return;
        }
 
-       /*
-        * Occasionally, e.g. on hard reset, our cache ends up as just filesystem
-        * meta-data with no content. Catch an easy case of this.
-        */
+       // Occasionally, e.g. on hard reset, our cache ends up as just filesystem
+       // meta-data with no content. Catch an easy case of this.
        if (st.st_size == 0) {
                cc_log("Invalid (empty) object file %s in cache", cached_obj);
                x_unlink(cached_obj);
@@ -1788,27 +1854,23 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
                if (st.st_size == 0) {
                        cc_log("Invalid (empty) dwo file %s in cache", cached_dwo);
                        x_unlink(cached_dwo);
-                       x_unlink(cached_obj); /* to really invalidate */
+                       x_unlink(cached_obj); // To really invalidate.
                        return;
                }
        }
 
-       /*
-        * (If mode != FROMCACHE_DIRECT_MODE, the dependency file is created by
-        * gcc.)
-        */
-       produce_dep_file = generating_dependencies && mode == FROMCACHE_DIRECT_MODE;
+       // (If mode != FROMCACHE_DIRECT_MODE, the dependency file is created by gcc.)
+       bool produce_dep_file =
+         generating_dependencies && mode == FROMCACHE_DIRECT_MODE;
 
-       /* If the dependency file should be in the cache, check that it is. */
+       // If the dependency file should be in the cache, check that it is.
        if (produce_dep_file && stat(cached_dep, &st) != 0) {
                cc_log("Dependency file %s missing in cache", cached_dep);
                return;
        }
 
-       /*
-        * Copy object file from cache. Do so also for FissionDwarf file, cached_dwo,
-        * when -gsplit-dwarf is specified.
-        */
+       // Copy object file from cache. Do so also for FissionDwarf file, cached_dwo,
+       // when -gsplit-dwarf is specified.
        if (!str_eq(output_obj, "/dev/null")) {
                get_file_from_cache(cached_obj, output_obj);
                if (using_split_dwarf) {
@@ -1820,15 +1882,15 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
                get_file_from_cache(cached_dep, output_dep);
        }
        if (generating_coverage && stat(cached_cov, &st) == 0 && st.st_size > 0) {
-               /* gcc won't generate notes if there is no code */
+               // The compiler won't generate notes if there is no code
                get_file_from_cache(cached_cov, output_cov);
        }
        if (output_dia) {
                get_file_from_cache(cached_dia, output_dia);
        }
 
-       /* Update modification timestamps to save files from LRU cleanup.
-        * Also gives files a sensible mtime when hard-linking. */
+       // Update modification timestamps to save files from LRU cleanup. Also gives
+       // files a sensible mtime when hard-linking.
        update_mtime(cached_obj);
        update_mtime(cached_stderr);
        if (produce_dep_file) {
@@ -1855,7 +1917,7 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
                update_manifest_file();
        }
 
-       /* log the cache hit */
+       // Log the cache hit.
        switch (mode) {
        case FROMCACHE_DIRECT_MODE:
                cc_log("Succeeded getting cached result");
@@ -1868,39 +1930,33 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest)
                break;
        }
 
-       /* and exit with the right status code */
+       // And exit with the right status code.
        x_exit(0);
 }
 
-/* find the real compiler. We just search the PATH to find an executable of the
- * same name that isn't a link to ourselves */
+// Find the real compiler. We just search the PATH to find an executable of the
+// same name that isn't a link to ourselves.
 static void
 find_compiler(char **argv)
 {
-       char *base;
-       char *compiler;
-
-       base = basename(argv[0]);
-
-       /* we might be being invoked like "ccache gcc -c foo.c" */
+       // We might be being invoked like "ccache gcc -c foo.c".
+       char *base = basename(argv[0]);
        if (same_executable_name(base, MYNAME)) {
                args_remove_first(orig_args);
                free(base);
                if (is_full_path(orig_args->argv[0])) {
-                       /* a full path was given */
+                       // A full path was given.
                        return;
                }
                base = basename(orig_args->argv[0]);
        }
 
-       /* support user override of the compiler */
+       // Support user override of the compiler.
        if (!str_eq(conf->compiler, "")) {
                base = conf->compiler;
        }
 
-       compiler = find_executable(base, MYNAME);
-
-       /* can't find the compiler! */
+       char *compiler = find_executable(base, MYNAME);
        if (!compiler) {
                stats_update(STATS_COMPILER);
                fatal("Could not find compiler \"%s\" in PATH", base);
@@ -1927,48 +1983,102 @@ color_output_possible(void)
        return isatty(STDERR_FILENO) && term_env && strcasecmp(term_env, "DUMB") != 0;
 }
 
-/*
- * Process the compiler options into options suitable for passing to the
- * preprocessor and the real compiler. The preprocessor options don't include
- * -E; this is added later. Returns true on success, otherwise false.
- */
+static bool
+detect_pch(const char *option, const char *arg, bool *found_pch)
+{
+       struct stat st;
+
+       // Try to be smart about detecting precompiled headers.
+       char *pch_file = NULL;
+       if (str_eq(option, "-include-pch") || str_eq(option, "-include-pth")) {
+               if (stat(arg, &st) == 0) {
+                       cc_log("Detected use of precompiled header: %s", arg);
+                       pch_file = x_strdup(arg);
+               }
+       } else {
+               char *gchpath = format("%s.gch", arg);
+               if (stat(gchpath, &st) == 0) {
+                       cc_log("Detected use of precompiled header: %s", gchpath);
+                       pch_file = x_strdup(gchpath);
+               } else {
+                       char *pchpath = format("%s.pch", arg);
+                       if (stat(pchpath, &st) == 0) {
+                               cc_log("Detected use of precompiled header: %s", pchpath);
+                               pch_file = x_strdup(pchpath);
+                       } else {
+                               // clang may use pretokenized headers.
+                               char *pthpath = format("%s.pth", arg);
+                               if (stat(pthpath, &st) == 0) {
+                                       cc_log("Detected use of pretokenized header: %s", pthpath);
+                                       pch_file = x_strdup(pthpath);
+                               }
+                               free(pthpath);
+                       }
+                       free(pchpath);
+               }
+               free(gchpath);
+       }
+
+       if (pch_file) {
+               if (included_pch_file) {
+                       cc_log("Multiple precompiled headers used: %s and %s\n",
+                              included_pch_file, pch_file);
+                       stats_update(STATS_ARGS);
+                       return false;
+               }
+               included_pch_file = pch_file;
+               *found_pch = true;
+       }
+       return true;
+}
+
+// Process the compiler options into options suitable for passing to the
+// preprocessor and the real compiler. The preprocessor options don't include
+// -E; this is added later. Returns true on success, otherwise false.
 bool
 cc_process_args(struct args *args, struct args **preprocessor_args,
                 struct args **compiler_args)
 {
-       int i;
        bool found_c_opt = false;
        bool found_S_opt = false;
-       bool found_arch_opt = false;
        bool found_pch = false;
        bool found_fpch_preprocess = false;
-       const char *explicit_language = NULL; /* As specified with -x. */
-       const char *file_language;            /* As deduced from file extension. */
-       const char *actual_language;          /* Language to actually use. */
+       const char *explicit_language = NULL; // As specified with -x.
+       const char *file_language;            // As deduced from file extension.
+       const char *actual_language;          // Language to actually use.
        const char *input_charset = NULL;
-       struct stat st;
-       /* is the dependency makefile name overridden with -MF? */
+       // Is the dependency makefile name overridden with -MF?
        bool dependency_filename_specified = false;
-       /* is the dependency makefile target name specified with -MT or -MQ? */
+       // Is the dependency makefile target name specified with -MT or -MQ?
        bool dependency_target_specified = false;
-       struct args *expanded_args, *stripped_args, *dep_args, *cpp_args;
-       int argc;
-       char **argv;
-       bool result = true;
-       bool found_color_diagnostics = false;
-
-       expanded_args = args_copy(args);
-       stripped_args = args_init(0, NULL);
-       dep_args = args_init(0, NULL);
-       cpp_args = args_init(0, NULL);
+       // expanded_args is a copy of the original arguments given to the compiler
+       // but with arguments from @file and similar constructs expanded. It's only
+       // used as a temporary data structure to loop over.
+       struct args *expanded_args = args_copy(args);
+       // stripped_args essentially contains all original arguments except those
+       // that only should be passed to the preprocessor (if run_second_cpp is
+       // false) and except dependency options (like -MD and friends).
+       struct args *stripped_args = args_init(0, NULL);
+       // cpp_args contains arguments that were not added to stripped_args, i.e.
+       // those that should only be passed to the preprocessor if run_second_cpp is
+       // false. If run_second_cpp is true, they will be passed to the compiler as
+       // well.
+       struct args *cpp_args = args_init(0, NULL);
+       // dep_args contains dependency options like -MD. They only passed to the
+       // preprocessor, never to the compiler.
+       struct args *dep_args = args_init(0, NULL);
 
-       argc = expanded_args->argc;
-       argv = expanded_args->argv;
+       bool found_color_diagnostics = false;
+       int debug_level = 0;
+       const char *debug_argument = NULL;
 
+       int argc = expanded_args->argc;
+       char **argv = expanded_args->argv;
        args_add(stripped_args, argv[0]);
 
-       for (i = 1; i < argc; i++) {
-               /* The user knows best: just swallow the next arg */
+       bool result = true;
+       for (int i = 1; i < argc; i++) {
+               // The user knows best: just swallow the next arg.
                if (str_eq(argv[i], "--ccache-skip")) {
                        i++;
                        if (i == argc) {
@@ -1980,22 +2090,21 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
 
-               /* Special case for -E. */
+               // Special case for -E.
                if (str_eq(argv[i], "-E")) {
                        stats_update(STATS_PREPROCESSING);
                        result = false;
                        goto out;
                }
 
-               /* Handle "@file" argument. */
+               // Handle "@file" argument.
                if (str_startswith(argv[i], "@") || str_startswith(argv[i], "-@")) {
                        char *argpath = argv[i] + 1;
-                       struct args *file_args;
 
                        if (argpath[-1] == '-') {
                                ++argpath;
                        }
-                       file_args = args_init_from_gcc_atfile(argpath);
+                       struct args *file_args = args_init_from_gcc_atfile(argpath);
                        if (!file_args) {
                                cc_log("Couldn't read arg file %s", argpath);
                                stats_update(STATS_ARGS);
@@ -2010,33 +2119,86 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
 
-               /* These are always too hard. */
-               if (compopt_too_hard(argv[i])
-                   || str_startswith(argv[i], "-fdump-")) {
+               // Handle cuda "-optf" and "--options-file" argument.
+               if (str_eq(argv[i], "-optf") || str_eq(argv[i], "--options-file")) {
+                       if (i > argc) {
+                               cc_log("Expected argument after -optf/--options-file");
+                               stats_update(STATS_UNSUPPORTED);
+                               result = false;
+                               goto out;
+                       }
+                       ++i;
+
+                       // Argument is a comma-separated list of files.
+                       char *str_start = argv[i];
+                       char *str_end = strchr(str_start, ',');
+                       int index = i + 1;
+
+                       if (!str_end) {
+                               str_end = str_start + strlen(str_start);
+                       }
+
+                       while (str_end) {
+                               *str_end = '\0';
+                               struct args *file_args = args_init_from_gcc_atfile(str_start);
+                               if (!file_args) {
+                                       cc_log("Couldn't read cuda options file %s", str_start);
+                                       stats_update(STATS_ARGS);
+                                       result = false;
+                                       goto out;
+                               }
+
+                               int new_index = file_args->argc + index;
+                               args_insert(expanded_args, index, file_args, false);
+                               index = new_index;
+                               str_start = str_end;
+                               str_end = strchr(str_start, ',');
+                       }
+
+                       argc = expanded_args->argc;
+                       argv = expanded_args->argv;
+                       continue;
+               }
+
+               // These are always too hard.
+               if (compopt_too_hard(argv[i]) || str_startswith(argv[i], "-fdump-")) {
                        cc_log("Compiler option %s is unsupported", argv[i]);
                        stats_update(STATS_UNSUPPORTED);
                        result = false;
                        goto out;
                }
 
-               /* These are too hard in direct mode. */
-               if (conf->direct_mode) {
-                       if (compopt_too_hard_for_direct_mode(argv[i])) {
-                               cc_log("Unsupported compiler option for direct mode: %s", argv[i]);
-                               conf->direct_mode = false;
-                       }
+               // These are too hard in direct mode.
+               if (conf->direct_mode && compopt_too_hard_for_direct_mode(argv[i])) {
+                       cc_log("Unsupported compiler option for direct mode: %s", argv[i]);
+                       conf->direct_mode = false;
+               }
+
+               // -Xarch_* options are too hard.
+               if (str_startswith(argv[i], "-Xarch_")) {
+                       cc_log("Unsupported compiler option :%s", argv[i]);
+                       stats_update(STATS_UNSUPPORTED);
+                       result = false;
+                       goto out;
                }
 
-               /* Multiple -arch options are too hard. */
+               // Handle -arch options.
                if (str_eq(argv[i], "-arch")) {
-                       if (found_arch_opt) {
-                               cc_log("More than one -arch compiler option is unsupported");
+                       if (arch_args_size == MAX_ARCH_ARGS - 1) {
+                               cc_log("Too many -arch compiler options; ccache supports at most %d",
+                                      MAX_ARCH_ARGS);
                                stats_update(STATS_UNSUPPORTED);
                                result = false;
                                goto out;
-                       } else {
-                               found_arch_opt = true;
                        }
+
+                       ++i;
+                       arch_args[arch_args_size] = x_strdup(argv[i]); // It will leak.
+                       ++arch_args_size;
+                       if (arch_args_size == 2) {
+                               conf->run_second_cpp = true;
+                       }
+                       continue;
                }
 
                if (str_eq(argv[i], "-fpch-preprocess")
@@ -2045,23 +2207,21 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        found_fpch_preprocess = true;
                }
 
-               /* we must have -c */
+               // We must have -c.
                if (str_eq(argv[i], "-c")) {
                        found_c_opt = true;
                        continue;
                }
 
-               /* -S changes the default extension */
+               // -S changes the default extension.
                if (str_eq(argv[i], "-S")) {
                        args_add(stripped_args, argv[i]);
                        found_S_opt = true;
                        continue;
                }
 
-               /*
-                * Special handling for -x: remember the last specified language before the
-                * input file and strip all -x options from the arguments.
-                */
+               // Special handling for -x: remember the last specified language before the
+               // input file and strip all -x options from the arguments.
                if (str_eq(argv[i], "-x")) {
                        if (i == argc-1) {
                                cc_log("Missing argument to %s", argv[i]);
@@ -2082,7 +2242,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
 
-               /* we need to work out where the output was meant to go */
+               // We need to work out where the output was meant to go.
                if (str_eq(argv[i], "-o")) {
                        if (i == argc-1) {
                                cc_log("Missing argument to %s", argv[i]);
@@ -2095,7 +2255,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
 
-               /* alternate form of -o, with no space */
+               // Alternate form of -o with no space.
                if (str_startswith(argv[i], "-o")) {
                        output_obj = make_relative_path(x_strdup(&argv[i][2]));
                        continue;
@@ -2107,41 +2267,58 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        args_add(stripped_args, argv[i]);
                        continue;
                }
+               if (str_startswith(argv[i], "-fdebug-prefix-map=")) {
+                       debug_prefix_map = x_strdup(argv[i] + 19);
+                       args_add(stripped_args, argv[i]);
+                       continue;
+               }
 
-               /* Debugging is handled specially, so that we know if we can strip line
-                * number info. */
+               // Debugging is handled specially, so that we know if we can strip line
+               // number info.
                if (str_startswith(argv[i], "-g")) {
-                       args_add(stripped_args, argv[i]);
-                       if (conf->unify && !str_eq(argv[i], "-g0")) {
-                               cc_log("%s used; disabling unify mode", argv[i]);
-                               conf->unify = false;
+                       const char *pLevel = argv[i] + 2;
+                       if (str_startswith(argv[i], "-ggdb")) {
+                               pLevel = argv[i] + 5;
+                       } else if (str_startswith(argv[i], "-gstabs")) {
+                               pLevel = argv[i] + 7;
+                       } else if (str_startswith(argv[i], "-gcoff")) {
+                               pLevel = argv[i] + 6;
+                       } else if (str_startswith(argv[i], "-gxcoff")) {
+                               pLevel = argv[i] + 7;
+                       } else if (str_startswith(argv[i], "-gvms")) {
+                               pLevel = argv[i] + 5;
                        }
-                       if (str_eq(argv[i], "-g3")) {
-                               /*
-                                * Fix for bug 7190 ("commandline macros (-D)
-                                * have non-zero lineno when using -g3").
-                                */
-                               cc_log("%s used; not compiling preprocessed code", argv[i]);
-                               conf->run_second_cpp = true;
+
+                       // Deduce level from argument, default is 2.
+                       int foundlevel = -1;
+                       if (pLevel[0] == '\0') {
+                               foundlevel = 2;
+                       } else if (pLevel[0] >= '0' && pLevel[0] <= '9') {
+                               foundlevel = atoi(pLevel);
+                       }
+
+                       if (foundlevel >= 0) {
+                               debug_level = foundlevel;
+                               debug_argument = argv[i];
+                               continue;
                        }
-                       continue;
                }
 
-               /* These options require special handling, because they
-                * behave differently with gcc -E, when the output
-                * file is not specified. */
+               // These options require special handling, because they behave differently
+               // with gcc -E, when the output file is not specified.
                if (str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD")) {
                        generating_dependencies = true;
                        args_add(dep_args, argv[i]);
                        continue;
                }
                if (str_startswith(argv[i], "-MF")) {
-                       char *arg;
-                       bool separate_argument = (strlen(argv[i]) == 3);
                        dependency_filename_specified = true;
                        free(output_dep);
+
+                       char *arg;
+                       bool separate_argument = (strlen(argv[i]) == 3);
                        if (separate_argument) {
-                               /* -MF arg */
+                               // -MF arg
                                if (i >= argc - 1) {
                                        cc_log("Missing argument to %s", argv[i]);
                                        stats_update(STATS_ARGS);
@@ -2151,11 +2328,11 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                                arg = argv[i + 1];
                                i++;
                        } else {
-                               /* -MFarg */
+                               // -MFarg
                                arg = &argv[i][3];
                        }
                        output_dep = make_relative_path(x_strdup(arg));
-                       /* Keep the format of the args the same */
+                       // Keep the format of the args the same.
                        if (separate_argument) {
                                args_add(dep_args, "-MF");
                                args_add(dep_args, output_dep);
@@ -2167,10 +2344,11 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
                if (str_startswith(argv[i], "-MQ") || str_startswith(argv[i], "-MT")) {
-                       char *relpath;
                        dependency_target_specified = true;
+
+                       char *relpath;
                        if (strlen(argv[i]) == 3) {
-                               /* -MQ arg or -MT arg */
+                               // -MQ arg or -MT arg
                                if (i >= argc - 1) {
                                        cc_log("Missing argument to %s", argv[i]);
                                        stats_update(STATS_ARGS);
@@ -2183,11 +2361,9 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                                free(relpath);
                                i++;
                        } else {
-                               char *arg_opt;
-                               char *option;
-                               arg_opt = x_strndup(argv[i], 3);
+                               char *arg_opt = x_strndup(argv[i], 3);
                                relpath = make_relative_path(x_strdup(argv[i] + 3));
-                               option = format("%s%s", arg_opt, relpath);
+                               char *option = format("%s%s", arg_opt, relpath);
                                args_add(dep_args, option);
                                free(arg_opt);
                                free(relpath);
@@ -2205,8 +2381,8 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        args_add(stripped_args, argv[i]);
                        continue;
                }
-               if (str_eq(argv[i], "--coverage") /* = -fprofile-arcs -ftest-coverage */
-                   || str_eq(argv[i], "-coverage")) { /* Undocumented but still works */
+               if (str_eq(argv[i], "--coverage") // = -fprofile-arcs -ftest-coverage
+                   || str_eq(argv[i], "-coverage")) { // Undocumented but still works.
                        profile_arcs = true;
                        generating_coverage = true;
                        args_add(stripped_args, argv[i]);
@@ -2226,10 +2402,12 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
                if (str_startswith(argv[i], "-Wp,")) {
-                       if (str_eq(argv[i], "-Wp,-P") || strstr(argv[i], ",-P,")) {
-                               /* -P removes preprocessor information in such a way that the object
-                                * file from compiling the preprocessed file will not be equal to the
-                                * object file produced when compiling without ccache. */
+                       if (str_eq(argv[i], "-Wp,-P")
+                           || strstr(argv[i], ",-P,")
+                           || str_endswith(argv[i], ",-P")) {
+                               // -P removes preprocessor information in such a way that the object
+                               // file from compiling the preprocessed file will not be equal to the
+                               // object file produced when compiling without ccache.
                                cc_log("Too hard option -Wp,-P detected");
                                stats_update(STATS_UNSUPPORTED);
                                failed();
@@ -2249,37 +2427,24 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                                output_dep = make_relative_path(x_strdup(argv[i] + 9));
                                args_add(dep_args, argv[i]);
                                continue;
-                       } else if (str_eq(argv[i], "-Wp,-MP")
-                                  || (strlen(argv[i]) > 8
-                                      && str_startswith(argv[i], "-Wp,-M")
-                                      && argv[i][7] == ','
-                                      && (argv[i][6] == 'F'
-                                          || argv[i][6] == 'Q'
-                                          || argv[i][6] == 'T')
-                                      && !strchr(argv[i] + 8, ','))) {
-                               /* TODO: Make argument to MF/MQ/MT relative. */
-                               args_add(dep_args, argv[i]);
+                       } else if (str_startswith(argv[i], "-Wp,-D")
+                                  && !strchr(argv[i] + 6, ',')) {
+                               // Treat it like -D.
+                               args_add(dep_args, argv[i] + 4);
                                continue;
                        } else if (conf->direct_mode) {
-                               /*
-                                * -Wp, can be used to pass too hard options to
-                                * the preprocessor. Hence, disable direct
-                                * mode.
-                                */
+                               // -Wp, can be used to pass too hard options to the preprocessor.
+                               // Hence, disable direct mode.
                                cc_log("Unsupported compiler option for direct mode: %s", argv[i]);
                                conf->direct_mode = false;
                        }
-
-                       /* Any other -Wp,* arguments are only relevant for the preprocessor. */
-                       args_add(cpp_args, argv[i]);
-                       continue;
                }
                if (str_eq(argv[i], "-MP")) {
                        args_add(dep_args, argv[i]);
                        continue;
                }
 
-               /* Input charset needs to be handled specially. */
+               // Input charset needs to be handled specially.
                if (str_startswith(argv[i], "-finput-charset=")) {
                        input_charset = argv[i];
                        continue;
@@ -2298,29 +2463,26 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                }
 
                if (str_startswith(argv[i], "-fprofile-")) {
-                       const char *arg_profile_dir = strchr(argv[i], '=');
                        char *arg = x_strdup(argv[i]);
-                       bool supported_profile_option = false;
-
+                       const char *arg_profile_dir = strchr(argv[i], '=');
                        if (arg_profile_dir) {
-                               char *option = x_strndup(argv[i], arg_profile_dir - argv[i]);
-                               char *dir;
-
-                               /* Convert to absolute path. */
-                               dir = x_realpath(arg_profile_dir + 1);
+                               // Convert to absolute path.
+                               char *dir = x_realpath(arg_profile_dir + 1);
                                if (!dir) {
-                                       /* Directory doesn't exist. */
+                                       // Directory doesn't exist.
                                        dir = x_strdup(arg_profile_dir + 1);
                                }
 
-                               /* We can get a better hit rate by using the real path here. */
+                               // We can get a better hit rate by using the real path here.
                                free(arg);
+                               char *option = x_strndup(argv[i], arg_profile_dir - argv[i]);
                                arg = format("%s=%s", option, dir);
                                cc_log("Rewriting %s to %s", argv[i], arg);
                                free(option);
                                free(dir);
                        }
 
+                       bool supported_profile_option = false;
                        if (str_startswith(argv[i], "-fprofile-generate")
                            || str_eq(argv[i], "-fprofile-arcs")) {
                                profile_generate = true;
@@ -2337,10 +2499,8 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                                args_add(stripped_args, arg);
                                free(arg);
 
-                               /*
-                                * If the profile directory has already been set, give up... Hard to
-                                * know what the user means, and what the compiler will do.
-                                */
+                               // If the profile directory has already been set, give up... Hard to
+                               // know what the user means, and what the compiler will do.
                                if (arg_profile_dir && profile_dir) {
                                        cc_log("Profile directory already set; giving up");
                                        result = false;
@@ -2367,7 +2527,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                }
                if (str_eq(argv[i], "-fdiagnostics-color=auto")) {
                        if (color_output_possible()) {
-                               /* Output is redirected, so color output must be forced. */
+                               // Output is redirected, so color output must be forced.
                                args_add(stripped_args, "-fdiagnostics-color=always");
                                cc_log("Automatically forcing colors");
                        } else {
@@ -2377,14 +2537,10 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
 
-               /*
-                * Options taking an argument that we may want to rewrite to relative paths
-                * to get better hit rate. A secondary effect is that paths in the standard
-                * error output produced by the compiler will be normalized.
-                */
+               // Options taking an argument that we may want to rewrite to relative paths
+               // to get better hit rate. A secondary effect is that paths in the standard
+               // error output produced by the compiler will be normalized.
                if (compopt_takes_path(argv[i])) {
-                       char *relpath;
-                       char *pch_file = NULL;
                        if (i == argc-1) {
                                cc_log("Missing argument to %s", argv[i]);
                                stats_update(STATS_ARGS);
@@ -2392,7 +2548,12 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                                goto out;
                        }
 
-                       relpath = make_relative_path(x_strdup(argv[i+1]));
+                       if (!detect_pch(argv[i], argv[i+1], &found_pch)) {
+                               result = false;
+                               goto out;
+                       }
+
+                       char *relpath = make_relative_path(x_strdup(argv[i+1]));
                        if (compopt_affects_cpp(argv[i])) {
                                args_add(cpp_args, argv[i]);
                                args_add(cpp_args, relpath);
@@ -2400,77 +2561,37 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                                args_add(stripped_args, argv[i]);
                                args_add(stripped_args, relpath);
                        }
-
-                       /* Try to be smart about detecting precompiled headers */
-                       if (str_eq(argv[i], "-include-pch")
-                           || str_eq(argv[i], "-include-pth")) {
-                               if (stat(argv[i+1], &st) == 0) {
-                                       cc_log("Detected use of precompiled header: %s", argv[i+1]);
-                                       found_pch = true;
-                                       pch_file = x_strdup(argv[i+1]);
-                               }
-                       } else {
-                               char *gchpath = format("%s.gch", argv[i+1]);
-                               if (stat(gchpath, &st) == 0) {
-                                       cc_log("Detected use of precompiled header: %s", gchpath);
-                                       found_pch = true;
-                                       pch_file = x_strdup(gchpath);
-                               } else {
-                                       char *pchpath = format("%s.pch", argv[i+1]);
-                                       if (stat(pchpath, &st) == 0) {
-                                               cc_log("Detected use of precompiled header: %s", pchpath);
-                                               found_pch = true;
-                                               pch_file = x_strdup(pchpath);
-                                       } else {
-                                               /* clang may use pretokenized headers */
-                                               char *pthpath = format("%s.pth", argv[i+1]);
-                                               if (stat(pthpath, &st) == 0) {
-                                                       cc_log("Detected use of pretokenized header: %s", pthpath);
-                                                       found_pch = true;
-                                                       pch_file = x_strdup(pthpath);
-                                               }
-                                               free(pthpath);
-                                       }
-                                       free(pchpath);
-                               }
-                               free(gchpath);
-                       }
-
-                       if (pch_file) {
-                               if (included_pch_file) {
-                                       cc_log("Multiple precompiled headers used: %s and %s\n",
-                                              included_pch_file, pch_file);
-                                       stats_update(STATS_ARGS);
-                                       result = false;
-                                       goto out;
-                               }
-                               included_pch_file = pch_file;
-                       }
-
                        free(relpath);
+
                        i++;
                        continue;
                }
 
-               /* Same as above but options with concatenated argument. */
-               if (compopt_short(compopt_takes_path, argv[i])) {
-                       char *relpath;
-                       char *option;
-                       relpath = make_relative_path(x_strdup(argv[i] + 2));
-                       option = format("-%c%s", argv[i][1], relpath);
-
-                       if (compopt_short(compopt_affects_cpp, argv[i])) {
-                               args_add(cpp_args, option);
-                       } else {
-                               args_add(stripped_args, option);
+               // Same as above but options with concatenated argument beginning with a
+               // slash.
+               if (argv[i][0] == '-') {
+                       char *slash_pos = strchr(argv[i], '/');
+                       if (slash_pos) {
+                               char *option = x_strndup(argv[i], slash_pos - argv[i]);
+                               if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) {
+                                       char *relpath = make_relative_path(x_strdup(slash_pos));
+                                       char *new_option = format("%s%s", option, relpath);
+                                       if (compopt_affects_cpp(option)) {
+                                               args_add(cpp_args, new_option);
+                                       } else {
+                                               args_add(stripped_args, new_option);
+                                       }
+                                       free(new_option);
+                                       free(relpath);
+                                       free(option);
+                                       continue;
+                               } else {
+                                       free(option);
+                               }
                        }
-
-                       free(relpath);
-                       free(option);
-                       continue;
                }
 
-               /* options that take an argument */
+               // Options that take an argument.
                if (compopt_takes_arg(argv[i])) {
                        if (i == argc-1) {
                                cc_log("Missing argument to %s", argv[i]);
@@ -2491,7 +2612,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
 
-               /* other options */
+               // Other options.
                if (argv[i][0] == '-') {
                        if (compopt_affects_cpp(argv[i])
                            || compopt_prefix_affects_cpp(argv[i])) {
@@ -2502,9 +2623,9 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        continue;
                }
 
-               /* if an argument isn't a plain file then assume its
-                * an option, not an input file. This allows us to
-                * cope better with unusual compiler options */
+               // If an argument isn't a plain file then assume its an option, not an
+               // input file. This allows us to cope better with unusual compiler options.
+               struct stat st;
                if (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode)) {
                        cc_log("%s is not a regular file, not considering as input file",
                               argv[i]);
@@ -2531,29 +2652,40 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                        goto out;
                }
 
-               /* The source code file path gets put into the notes */
+               // The source code file path gets put into the notes.
                if (generating_coverage) {
                        input_file = x_strdup(argv[i]);
                        continue;
                }
 
-               lstat(argv[i], &st);
-               if (S_ISLNK(st.st_mode)) {
-                       /* Don't rewrite source file path if it's a symlink since
-                          make_relative_path resolves symlinks using realpath(3) and this leads
-                          to potentially choosing incorrect relative header files. See the
-                          "symlink to source file" test. */
+               if (is_symlink(argv[i])) {
+                       // Don't rewrite source file path if it's a symlink since
+                       // make_relative_path resolves symlinks using realpath(3) and this leads
+                       // to potentially choosing incorrect relative header files. See the
+                       // "symlink to source file" test.
                        input_file = x_strdup(argv[i]);
                } else {
-                       /* Rewrite to relative to increase hit rate. */
+                       // Rewrite to relative to increase hit rate.
                        input_file = make_relative_path(x_strdup(argv[i]));
                }
-       } /* for */
+       } // for
+
+       if (debug_level > 0) {
+               generating_debuginfo = true;
+               args_add(stripped_args, debug_argument);
+               if (conf->unify) {
+                       cc_log("%s used; disabling unify mode", debug_argument);
+                       conf->unify = false;
+               }
+               if (debug_level >= 3 && !conf->run_second_cpp) {
+                       cc_log("%s used; not compiling preprocessed code", debug_argument);
+                       conf->run_second_cpp = true;
+               }
+       }
 
        if (found_S_opt) {
-               /* Even if -gsplit-dwarf is given, the .dwo file is not generated when -S
-                * is also given.
-                */
+               // Even if -gsplit-dwarf is given, the .dwo file is not generated when -S
+               // is also given.
                using_split_dwarf = false;
                cc_log("Disabling caching of dwarf files since -S is used");
        }
@@ -2605,13 +2737,13 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                goto out;
        }
 
-       if (!found_c_opt) {
+       if (!found_c_opt && !found_S_opt) {
                if (output_is_precompiled_header) {
                        args_add(stripped_args, "-c");
                } else {
                        cc_log("No -c option found");
-                       /* I find that having a separate statistic for autoconf tests is useful,
-                        * as they are the dominant form of "called for link" in many cases */
+                       // I find that having a separate statistic for autoconf tests is useful,
+                       // as they are the dominant form of "called for link" in many cases.
                        if (strstr(input_file, "conftest.")) {
                                stats_update(STATS_CONFTEST);
                        } else {
@@ -2631,8 +2763,8 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
 
        direct_i_file = language_is_preprocessed(actual_language);
 
-       if (output_is_precompiled_header) {
-               /* It doesn't work to create the .gch from preprocessed source. */
+       if (output_is_precompiled_header && !conf->run_second_cpp) {
+               // It doesn't work to create the .gch from preprocessed source.
                cc_log("Creating precompiled header; not compiling preprocessed code");
                conf->run_second_cpp = true;
        }
@@ -2643,7 +2775,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                conf->cpp_extension = x_strdup(extension_for_language(p_language) + 1);
        }
 
-       /* don't try to second guess the compilers heuristics for stdout handling */
+       // Don't try to second guess the compilers heuristics for stdout handling.
        if (output_obj && str_eq(output_obj, "-")) {
                stats_update(STATS_OUTSTDOUT);
                cc_log("Output file is -");
@@ -2655,9 +2787,8 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                if (output_is_precompiled_header) {
                        output_obj = format("%s.gch", input_file);
                } else {
-                       char *p;
                        output_obj = basename(input_file);
-                       p = strrchr(output_obj, '.');
+                       char *p = strrchr(output_obj, '.');
                        if (!p || !p[1]) {
                                cc_log("Badly formed object filename");
                                stats_update(STATS_ARGS);
@@ -2670,22 +2801,21 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
        }
 
        if (using_split_dwarf) {
-               char *p;
-               p = strrchr(output_obj, '.');
+               char *p = strrchr(output_obj, '.');
                if (!p || !p[1]) {
                        cc_log("Badly formed object filename");
                        stats_update(STATS_ARGS);
                        result = false;
                        goto out;
                }
-               {
-                       char *base_name = remove_extension(output_obj);
-                       output_dwo = format("%s.dwo", base_name);
-                       free(base_name);
-               }
+
+               char *base_name = remove_extension(output_obj);
+               output_dwo = format("%s.dwo", base_name);
+               free(base_name);
        }
 
-       /* cope with -o /dev/null */
+       // Cope with -o /dev/null.
+       struct stat st;
        if (!str_eq(output_obj, "/dev/null")
            && stat(output_obj, &st) == 0
            && !S_ISREG(st.st_mode)) {
@@ -2695,13 +2825,11 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                goto out;
        }
 
-       /*
-        * Some options shouldn't be passed to the real compiler when it compiles
-        * preprocessed code:
-        *
-        * -finput-charset=XXX (otherwise conversion happens twice)
-        * -x XXX (otherwise the wrong language is selected)
-        */
+       // Some options shouldn't be passed to the real compiler when it compiles
+       // preprocessed code:
+       //
+       // -finput-charset=XXX (otherwise conversion happens twice)
+       // -x XXX (otherwise the wrong language is selected)
        if (input_charset) {
                args_add(cpp_args, input_charset);
        }
@@ -2713,21 +2841,19 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                args_add(cpp_args, explicit_language);
        }
 
-       /*
-        * Since output is redirected, compilers will not color their output by
-        * default, so force it explicitly if it would be otherwise done.
-        */
+       // Since output is redirected, compilers will not color their output by
+       // default, so force it explicitly if it would be otherwise done.
        if (!found_color_diagnostics && color_output_possible()) {
                if (compiler_is_clang(args)) {
-                       args_add(stripped_args, "-fcolor-diagnostics");
-                       cc_log("Automatically enabling colors");
+                       if (!str_eq(actual_language, "assembler")) {
+                               args_add(stripped_args, "-fcolor-diagnostics");
+                               cc_log("Automatically enabling colors");
+                       }
                } else if (compiler_is_gcc(args)) {
-                       /*
-                        * GCC has it since 4.9, but that'd require detecting what GCC version is
-                        * used for the actual compile. However it requires also GCC_COLORS to be
-                        * set (and not empty), so use that for detecting if GCC would use
-                        * colors.
-                        */
+                       // GCC has it since 4.9, but that'd require detecting what GCC version is
+                       // used for the actual compile. However it requires also GCC_COLORS to be
+                       // set (and not empty), so use that for detecting if GCC would use
+                       // colors.
                        if (getenv("GCC_COLORS") && getenv("GCC_COLORS")[0] != '\0') {
                                args_add(stripped_args, "-fdiagnostics-color");
                                cc_log("Automatically enabling colors");
@@ -2735,16 +2861,11 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                }
        }
 
-       /*
-        * Add flags for dependency generation only to the preprocessor command line.
-        */
+       // Add flags for dependency generation only to the preprocessor command line.
        if (generating_dependencies) {
                if (!dependency_filename_specified) {
-                       char *default_depfile_name;
-                       char *base_name;
-
-                       base_name = remove_extension(output_obj);
-                       default_depfile_name = format("%s.d", base_name);
+                       char *base_name = remove_extension(output_obj);
+                       char *default_depfile_name = format("%s.d", base_name);
                        free(base_name);
                        args_add(dep_args, "-MF");
                        args_add(dep_args, default_depfile_name);
@@ -2757,11 +2878,8 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
                }
        }
        if (generating_coverage) {
-               char *default_covfile_name;
-               char *base_name;
-
-               base_name = remove_extension(output_obj);
-               default_covfile_name = format("%s.gcno", base_name);
+               char *base_name = remove_extension(output_obj);
+               char *default_covfile_name = format("%s.gcno", base_name);
                free(base_name);
                output_cov = make_relative_path(x_strdup(default_covfile_name));
        }
@@ -2769,27 +2887,26 @@ cc_process_args(struct args *args, struct args **preprocessor_args,
        *compiler_args = args_copy(stripped_args);
        if (conf->run_second_cpp) {
                args_extend(*compiler_args, cpp_args);
-       } else {
-               if (explicit_language) {
-                       /*
-                        * Workaround for a bug in Apple's patched distcc -- it doesn't properly
-                        * reset the language specified with -x, so if -x is given, we have to
-                        * specify the preprocessed language explicitly.
-                        */
-                       args_add(*compiler_args, "-x");
-                       args_add(*compiler_args, p_language_for_language(explicit_language));
-               }
+       } else if (explicit_language) {
+               // Workaround for a bug in Apple's patched distcc -- it doesn't properly
+               // reset the language specified with -x, so if -x is given, we have to
+               // specify the preprocessed language explicitly.
+               args_add(*compiler_args, "-x");
+               args_add(*compiler_args, p_language_for_language(explicit_language));
        }
 
        if (found_c_opt) {
                args_add(*compiler_args, "-c");
        }
 
-       /*
-        * Only pass dependency arguments to the preprocesor since Intel's C++
-        * compiler doesn't produce a correct .d file when compiling preprocessed
-        * source.
-        */
+       for (size_t i = 0; i < arch_args_size; ++i) {
+               args_add(*compiler_args, "-arch");
+               args_add(*compiler_args, arch_args[i]);
+       }
+
+       // Only pass dependency arguments to the preprocesor since Intel's C++
+       // compiler doesn't produce a correct .d file when compiling preprocessed
+       // source.
        args_extend(cpp_args, dep_args);
 
        *preprocessor_args = args_copy(stripped_args);
@@ -2806,20 +2923,17 @@ out:
 static void
 create_initial_config_file(struct conf *conf, const char *path)
 {
-       unsigned max_files;
-       uint64_t max_size;
-       char *stats_dir;
-       FILE *f;
-       struct stat st;
-
        if (create_parent_dirs(path) != 0) {
                return;
        }
 
-       stats_dir = format("%s/0", conf->cache_dir);
+       unsigned max_files;
+       uint64_t max_size;
+       char *stats_dir = format("%s/0", conf->cache_dir);
+       struct stat st;
        if (stat(stats_dir, &st) == 0) {
                stats_get_obsolete_limits(stats_dir, &max_files, &max_size);
-               /* STATS_MAXFILES and STATS_MAXSIZE was stored for each top directory. */
+               // STATS_MAXFILES and STATS_MAXSIZE was stored for each top directory.
                max_files *= 16;
                max_size *= 16;
        } else {
@@ -2828,7 +2942,7 @@ create_initial_config_file(struct conf *conf, const char *path)
        }
        free(stats_dir);
 
-       f = fopen(path, "w");
+       FILE *f = fopen(path, "w");
        if (!f) {
                return;
        }
@@ -2845,22 +2959,17 @@ create_initial_config_file(struct conf *conf, const char *path)
        fclose(f);
 }
 
-/*
- * Read config file(s), populate variables, create configuration file in cache
- * directory if missing, etc.
- */
+// Read config file(s), populate variables, create configuration file in cache
+// directory if missing, etc.
 static void
 initialize(void)
 {
-       char *errmsg;
-       char *p;
-       struct stat st;
-       bool should_create_initial_config = false;
-
        conf_free(conf);
        conf = conf_create();
 
-       p = getenv("CCACHE_CONFIGPATH");
+       char *errmsg;
+       struct stat st;
+       char *p = getenv("CCACHE_CONFIGPATH");
        if (p) {
                primary_config_path = x_strdup(p);
        } else {
@@ -2869,7 +2978,7 @@ initialize(void)
                        if (stat(secondary_config_path, &st) == 0) {
                                fatal("%s", errmsg);
                        }
-                       /* Missing config file in SYSCONFDIR is OK. */
+                       // Missing config file in SYSCONFDIR is OK.
                        free(errmsg);
                }
 
@@ -2887,6 +2996,7 @@ initialize(void)
                primary_config_path = format("%s/ccache.conf", conf->cache_dir);
        }
 
+       bool should_create_initial_config = false;
        if (!conf_read(conf, primary_config_path, &errmsg)) {
                if (stat(primary_config_path, &st) == 0) {
                        fatal("%s", errmsg);
@@ -2918,7 +3028,7 @@ initialize(void)
        }
 }
 
-/* Reset the global state. Used by the test suite. */
+// Reset the global state. Used by the test suite.
 void
 cc_reset(void)
 {
@@ -2926,6 +3036,7 @@ cc_reset(void)
        free(primary_config_path); primary_config_path = NULL;
        free(secondary_config_path); secondary_config_path = NULL;
        free(current_working_dir); current_working_dir = NULL;
+       free(debug_prefix_map); debug_prefix_map = NULL;
        free(profile_dir); profile_dir = NULL;
        free(included_pch_file); included_pch_file = NULL;
        args_free(orig_args); orig_args = NULL;
@@ -2944,9 +3055,17 @@ cc_reset(void)
        free(cached_dia); cached_dia = NULL;
        free(manifest_path); manifest_path = NULL;
        time_of_compilation = 0;
+       for (size_t i = 0; i < ignore_headers_len; i++) {
+               free(ignore_headers[i]);
+               ignore_headers[i] = NULL;
+       }
+       free(ignore_headers); ignore_headers = NULL;
+       ignore_headers_len = 0;
        if (included_files) {
                hashtable_destroy(included_files, 1); included_files = NULL;
        }
+       has_absolute_include_headers = false;
+       generating_debuginfo = false;
        generating_dependencies = false;
        generating_coverage = false;
        profile_arcs = false;
@@ -2961,23 +3080,19 @@ cc_reset(void)
        using_split_dwarf = false;
 }
 
-/* Make a copy of stderr that will not be cached, so things like
- * distcc can send networking errors to it. */
+// Make a copy of stderr that will not be cached, so things like distcc can
+// send networking errors to it.
 static void
 setup_uncached_err(void)
 {
-       char *buf;
-       int uncached_fd;
-
-       uncached_fd = dup(2);
+       int uncached_fd = dup(2);
        if (uncached_fd == -1) {
                cc_log("dup(2) failed: %s", strerror(errno));
                failed();
        }
 
-       /* leak a pointer to the environment */
-       buf = format("UNCACHED_ERR_FD=%d", uncached_fd);
-
+       // Leak a pointer to the environment.
+       char *buf = format("UNCACHED_ERR_FD=%d", uncached_fd);
        if (putenv(buf) == -1) {
                cc_log("putenv failed: %s", strerror(errno));
                failed();
@@ -2991,24 +3106,13 @@ configuration_logger(const char *descr, const char *origin, void *context)
        cc_bulklog("Config: (%s) %s", origin, descr);
 }
 
-/* the main ccache driver function */
+// The main ccache driver function.
 static void
 ccache(int argc, char *argv[])
 {
-       bool put_object_in_manifest = false;
-       struct file_hash *object_hash;
-       struct file_hash *object_hash_from_manifest = NULL;
-       struct mdfour common_hash;
-       struct mdfour direct_hash;
-       struct mdfour cpp_hash;
-
-       /* Arguments (except -E) to send to the preprocessor. */
-       struct args *preprocessor_args;
-
-       /* Arguments to send to the real compiler. */
-       struct args *compiler_args;
-
+#ifndef _WIN32
        set_up_signal_handlers();
+#endif
 
        orig_args = args_init(argc, argv);
 
@@ -3039,6 +3143,12 @@ ccache(int argc, char *argv[])
                conf->direct_mode = false;
        }
 
+       conf->limit_multiple = MIN(MAX(conf->limit_multiple, 0.0), 1.0);
+
+       // Arguments (except -E) to send to the preprocessor.
+       struct args *preprocessor_args;
+       // Arguments to send to the real compiler.
+       struct args *compiler_args;
        if (!cc_process_args(orig_args, &preprocessor_args, &compiler_args)) {
                failed();
        }
@@ -3068,33 +3178,31 @@ ccache(int argc, char *argv[])
 
        cc_log("Object file: %s", output_obj);
 
+       struct mdfour common_hash;
        hash_start(&common_hash);
        calculate_common_hash(preprocessor_args, &common_hash);
 
-       /* try to find the hash using the manifest */
-       direct_hash = common_hash;
+       // Try to find the hash using the manifest.
+       struct mdfour direct_hash = common_hash;
+       bool put_object_in_manifest = false;
+       struct file_hash *object_hash = NULL;
+       struct file_hash *object_hash_from_manifest = NULL;
        if (conf->direct_mode) {
                cc_log("Trying direct lookup");
                object_hash = calculate_object_hash(preprocessor_args, &direct_hash, 1);
                if (object_hash) {
                        update_cached_result_globals(object_hash);
 
-                       /*
-                        * If we can return from cache at this point then do
-                        * so.
-                        */
+                       // If we can return from cache at this point then do so.
                        from_cache(FROMCACHE_DIRECT_MODE, 0);
 
-                       /*
-                        * Wasn't able to return from cache at this point.
-                        * However, the object was already found in manifest,
-                        * so don't readd it later.
-                        */
+                       // Wasn't able to return from cache at this point. However, the object
+                       // was already found in manifest, so don't readd it later.
                        put_object_in_manifest = false;
 
                        object_hash_from_manifest = object_hash;
                } else {
-                       /* Add object to manifest later. */
+                       // Add object to manifest later.
                        put_object_in_manifest = true;
                }
        }
@@ -3104,11 +3212,8 @@ ccache(int argc, char *argv[])
                failed();
        }
 
-       /*
-        * Find the hash using the preprocessed output. Also updates
-        * included_files.
-        */
-       cpp_hash = common_hash;
+       // Find the hash using the preprocessed output. Also updates included_files.
+       struct mdfour cpp_hash = common_hash;
        object_hash = calculate_object_hash(preprocessor_args, &cpp_hash, 0);
        if (!object_hash) {
                fatal("internal error: object hash from cpp returned NULL");
@@ -3117,21 +3222,18 @@ ccache(int argc, char *argv[])
 
        if (object_hash_from_manifest
            && !file_hashes_equal(object_hash_from_manifest, object_hash)) {
-               /*
-                * The hash from manifest differs from the hash of the
-                * preprocessor output. This could be because:
-                *
-                * - The preprocessor produces different output for the same
-                *   input (not likely).
-                * - There's a bug in ccache (maybe incorrect handling of
-                *   compiler arguments).
-                * - The user has used a different CCACHE_BASEDIR (most
-                *   likely).
-                *
-                * The best thing here would probably be to remove the hash
-                * entry from the manifest. For now, we use a simpler method:
-                * just remove the manifest file.
-                */
+               // The hash from manifest differs from the hash of the preprocessor output.
+               // This could be because:
+               //
+               // - The preprocessor produces different output for the same input (not
+               //   likely).
+               // - There's a bug in ccache (maybe incorrect handling of compiler
+               //   arguments).
+               // - The user has used a different CCACHE_BASEDIR (most likely).
+               //
+               // The best thing here would probably be to remove the hash entry from the
+               // manifest. For now, we use a simpler method: just remove the manifest
+               // file.
                cc_log("Hash from manifest doesn't match preprocessor output");
                cc_log("Likely reason: different CCACHE_BASEDIRs used");
                cc_log("Removing manifest as a safety measure");
@@ -3140,7 +3242,7 @@ ccache(int argc, char *argv[])
                put_object_in_manifest = true;
        }
 
-       /* if we can return from cache at this point then do */
+       // If we can return from cache at this point then do.
        from_cache(FROMCACHE_CPP_MODE, put_object_in_manifest);
 
        if (conf->read_only) {
@@ -3148,9 +3250,9 @@ ccache(int argc, char *argv[])
                failed();
        }
 
-       add_prefix(compiler_args);
+       add_prefix(compiler_args, conf->prefix_command);
 
-       /* run real compiler, sending output to cache */
+       // Run real compiler, sending output to cache.
        to_cache(compiler_args);
 
        x_exit(0);
@@ -3163,13 +3265,10 @@ configuration_printer(const char *descr, const char *origin, void *context)
        fprintf(context, "(%s) %s\n", origin, descr);
 }
 
-/* the main program when not doing a compile */
+// The main program when not doing a compile.
 static int
 ccache_main_options(int argc, char *argv[])
 {
-       int c;
-       char *errmsg;
-
        enum longopts {
                DUMP_MANIFEST
        };
@@ -3188,35 +3287,36 @@ ccache_main_options(int argc, char *argv[])
                {0, 0, 0, 0}
        };
 
+       int c;
        while ((c = getopt_long(argc, argv, "cChF:M:o:psVz", options, NULL)) != -1) {
                switch (c) {
                case DUMP_MANIFEST:
                        manifest_dump(optarg, stdout);
                        break;
 
-               case 'c': /* --cleanup */
+               case 'c': // --cleanup
                        initialize();
                        cleanup_all(conf);
                        printf("Cleaned cache\n");
                        break;
 
-               case 'C': /* --clear */
+               case 'C': // --clear
                        initialize();
                        wipe_all(conf);
                        printf("Cleared cache\n");
                        break;
 
-               case 'h': /* --help */
+               case 'h': // --help
                        fputs(USAGE_TEXT, stdout);
                        x_exit(0);
 
-               case 'F': /* --max-files */
+               case 'F': // --max-files
                {
-                       unsigned files;
                        initialize();
-                       files = atoi(optarg);
+                       char *errmsg;
                        if (conf_set_value_in_file(primary_config_path, "max_files", optarg,
                                                   &errmsg)) {
+                               unsigned files = atoi(optarg);
                                if (files == 0) {
                                        printf("Unset cache file limit\n");
                                } else {
@@ -3228,13 +3328,14 @@ ccache_main_options(int argc, char *argv[])
                }
                break;
 
-               case 'M': /* --max-size */
+               case 'M': // --max-size
                {
-                       uint64_t size;
                        initialize();
+                       uint64_t size;
                        if (!parse_size_with_suffix(optarg, &size)) {
                                fatal("invalid size: %s", optarg);
                        }
+                       char *errmsg;
                        if (conf_set_value_in_file(primary_config_path, "max_size", optarg,
                                                   &errmsg)) {
                                if (size == 0) {
@@ -3250,16 +3351,16 @@ ccache_main_options(int argc, char *argv[])
                }
                break;
 
-               case 'o': /* --set-config */
+               case 'o': // --set-config
                {
-                       char *errmsg, *key, *value, *p;
                        initialize();
-                       p = strchr(optarg, '=');
+                       char *p = strchr(optarg, '=');
                        if (!p) {
                                fatal("missing equal sign in \"%s\"", optarg);
                        }
-                       key = x_strndup(optarg, p - optarg);
-                       value = p + 1;
+                       char *key = x_strndup(optarg, p - optarg);
+                       char *value = p + 1;
+                       char *errmsg;
                        if (!conf_set_value_in_file(primary_config_path, key, value, &errmsg)) {
                                fatal("%s", errmsg);
                        }
@@ -3267,21 +3368,21 @@ ccache_main_options(int argc, char *argv[])
                }
                break;
 
-               case 'p': /* --print-config */
+               case 'p': // --print-config
                        initialize();
                        conf_print_items(conf, configuration_printer, stdout);
                        break;
 
-               case 's': /* --show-stats */
+               case 's': // --show-stats
                        initialize();
                        stats_summary(conf);
                        break;
 
-               case 'V': /* --version */
+               case 'V': // --version
                        fprintf(stdout, VERSION_TEXT, CCACHE_VERSION);
                        x_exit(0);
 
-               case 'z': /* --zero-stats */
+               case 'z': // --zero-stats
                        initialize();
                        stats_zero();
                        printf("Statistics cleared\n");
@@ -3299,15 +3400,15 @@ ccache_main_options(int argc, char *argv[])
 int
 ccache_main(int argc, char *argv[])
 {
-       /* check if we are being invoked as "ccache" */
+       // Check if we are being invoked as "ccache".
        char *program_name = basename(argv[0]);
        if (same_executable_name(program_name, MYNAME)) {
                if (argc < 2) {
                        fputs(USAGE_TEXT, stderr);
                        x_exit(1);
                }
-               /* if the first argument isn't an option, then assume we are
-                * being passed a compiler name and options */
+               // If the first argument isn't an option, then assume we are being passed a
+               // compiler name and options.
                if (argv[1][0] == '-') {
                        return ccache_main_options(argc, argv);
                }
index ca96d3b..7b29bb8 100644 (file)
--- a/ccache.h
+++ b/ccache.h
@@ -20,7 +20,7 @@
 
 extern const char CCACHE_VERSION[];
 
-/* statistics fields in storage order */
+// Statistics fields in storage order.
 enum stats {
        STATS_NONE = 0,
        STATS_STDOUT = 1,
@@ -51,6 +51,7 @@ enum stats {
        STATS_COMPCHECK = 26,
        STATS_CANTUSEPCH = 27,
        STATS_PREPROCESSING = 28,
+       STATS_NUMCLEANUPS = 29,
 
        STATS_END
 };
@@ -60,17 +61,25 @@ enum stats {
 #define SLOPPY_FILE_MACRO 4
 #define SLOPPY_TIME_MACROS 8
 #define SLOPPY_PCH_DEFINES 16
-/*
- * Allow us to match files based on their stats (size, mtime, ctime), without
- * looking at their contents.
- */
+// Allow us to match files based on their stats (size, mtime, ctime), without
+// looking at their contents.
 #define SLOPPY_FILE_STAT_MATCHES 32
+// Allow us to not include any system headers in the manifest include files,
+// similar to -MM versus -M for dependencies.
+#define SLOPPY_NO_SYSTEM_HEADERS 64
 
 #define str_eq(s1, s2) (strcmp((s1), (s2)) == 0)
-#define str_startswith(s, p) (strncmp((s), (p), strlen((p))) == 0)
+#define str_startswith(s, prefix) \
+       (strncmp((s), (prefix), strlen((prefix))) == 0)
+#define str_endswith(s, suffix) \
+       (strlen(s) >= strlen(suffix) \
+        && str_eq((s) + strlen(s) - strlen(suffix), (suffix)))
 
-/* ------------------------------------------------------------------------- */
-/* args.c */
+// Buffer size for I/O operations. Should be a multiple of 4 KiB.
+#define READ_BUFFER_SIZE 65536
+
+// ----------------------------------------------------------------------------
+// args.c
 
 struct args {
        char **argv;
@@ -93,8 +102,8 @@ void args_remove_first(struct args *args);
 char *args_to_string(struct args *args);
 bool args_equal(struct args *args1, struct args *args2);
 
-/* ------------------------------------------------------------------------- */
-/* hash.c */
+// ----------------------------------------------------------------------------
+// hash.c
 
 void hash_start(struct mdfour *md);
 void hash_buffer(struct mdfour *md, const void *s, size_t len);
@@ -108,8 +117,8 @@ void hash_int(struct mdfour *md, int x);
 bool hash_fd(struct mdfour *md, int fd);
 bool hash_file(struct mdfour *md, const char *fname);
 
-/* ------------------------------------------------------------------------- */
-/* util.c */
+// ----------------------------------------------------------------------------
+// util.c
 
 void cc_log(const char *format, ...) ATTR_FORMAT(printf, 1, 2);
 void cc_bulklog(const char *format, ...) ATTR_FORMAT(printf, 1, 2);
@@ -162,6 +171,7 @@ size_t common_dir_prefix_length(const char *s1, const char *s2);
 char *get_relative_path(const char *from, const char *to);
 bool is_absolute_path(const char *path);
 bool is_full_path(const char *path);
+bool is_symlink(const char *path);
 void update_mtime(const char *path);
 void x_exit(int status) ATTR_NORETURN;
 int x_rename(const char *oldpath, const char *newpath);
@@ -174,8 +184,8 @@ bool read_file(const char *path, size_t size_hint, char **data, size_t *size);
 char *read_text_file(const char *path, size_t size_hint);
 char *subst_env_in_string(const char *str, char **errmsg);
 
-/* ------------------------------------------------------------------------- */
-/* stats.c */
+// ----------------------------------------------------------------------------
+// stats.c
 
 void stats_update(enum stats stat);
 void stats_flush(void);
@@ -186,44 +196,45 @@ void stats_update_size(uint64_t size, unsigned files);
 void stats_get_obsolete_limits(const char *dir, unsigned *maxfiles,
                                uint64_t *maxsize);
 void stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size);
+void stats_add_cleanup(const char *dir, unsigned count);
 void stats_read(const char *path, struct counters *counters);
 void stats_write(const char *path, struct counters *counters);
 
-/* ------------------------------------------------------------------------- */
-/* unify.c */
+// ----------------------------------------------------------------------------
+// unify.c
 
 int unify_hash(struct mdfour *hash, const char *fname);
 
-/* ------------------------------------------------------------------------- */
-/* exitfn.c */
+// ----------------------------------------------------------------------------
+// exitfn.c
 
 void exitfn_init(void);
 void exitfn_add_nullary(void (*function)(void));
 void exitfn_add(void (*function)(void *), void *context);
 void exitfn_call(void);
 
-/* ------------------------------------------------------------------------- */
-/* cleanup.c */
+// ----------------------------------------------------------------------------
+// cleanup.c
 
 void cleanup_dir(struct conf *conf, const char *dir);
 void cleanup_all(struct conf *conf);
 void wipe_all(struct conf *conf);
 
-/* ------------------------------------------------------------------------- */
-/* execute.c */
+// ----------------------------------------------------------------------------
+// execute.c
 
 int execute(char **argv, int fd_out, int fd_err, pid_t *pid);
 char *find_executable(const char *name, const char *exclude_name);
 void print_command(FILE *fp, char **argv);
 
-/* ------------------------------------------------------------------------- */
-/* lockfile.c */
+// ----------------------------------------------------------------------------
+// lockfile.c
 
 bool lockfile_acquire(const char *path, unsigned staleness_limit);
 void lockfile_release(const char *path);
 
-/* ------------------------------------------------------------------------- */
-/* ccache.c */
+// ----------------------------------------------------------------------------
+// ccache.c
 
 extern time_t time_of_compilation;
 void block_signals(void);
@@ -233,7 +244,7 @@ bool cc_process_args(struct args *args, struct args **preprocessor_args,
 void cc_reset(void);
 bool is_precompiled_header(const char *path);
 
-/* ------------------------------------------------------------------------- */
+// ----------------------------------------------------------------------------
 
 #if HAVE_COMPAR_FN_T
 #define COMPAR_FN_T __compar_fn_t
@@ -241,13 +252,12 @@ bool is_precompiled_header(const char *path);
 typedef int (*COMPAR_FN_T)(const void *, const void *);
 #endif
 
-/* work with silly DOS binary open */
+// Work with silly DOS binary open.
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-/* mkstemp() on some versions of cygwin don't handle binary files, so
-   override */
+// mkstemp() on some versions of cygwin don't handle binary files, so override.
 #ifdef __CYGWIN__
 #undef HAVE_MKSTEMP
 #endif
@@ -267,16 +277,21 @@ void add_exe_ext_if_no_to_fullpath(char *full_path_win_ext, size_t max_size,
 #    define link(src,dst) (CreateHardLink(dst,src,NULL) ? 0 : -1)
 #    define lstat(a,b) stat(a,b)
 #    define execv(a,b) win32execute(a,b,0,-1,-1)
-#    define execute(a,b,c) win32execute(*(a),a,1,b,c)
+#    define execute(a,b,c,d) win32execute(*(a),a,1,b,c)
+#    define DIR_DELIM_CH '/'
 #    define PATH_DELIM ";"
 #    define F_RDLCK 0
 #    define F_WRLCK 0
 #else
+#    define DIR_DELIM_CH '\\'
 #    define PATH_DELIM ":"
 #endif
 
 #ifndef MAX
 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 #endif
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
 
-#endif /* ifndef CCACHE_H */
+#endif // ifndef CCACHE_H
index dcb1a15..428a64b 100644 (file)
--- a/cleanup.c
+++ b/cleanup.c
@@ -1,45 +1,36 @@
-/*
- * Copyright (C) 2002-2006 Andrew Tridgell
- * Copyright (C) 2009-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2002-2006 Andrew Tridgell
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
-/*
- * When "max files" or "max cache size" is reached, one of the 16 cache
- * subdirectories is cleaned up. When doing so, files are deleted (in LRU
- * order) until the levels are below LIMIT_MULTIPLE.
- */
-#define LIMIT_MULTIPLE 0.8
-
 static struct files {
        char *fname;
        time_t mtime;
        uint64_t size;
 } **files;
-static unsigned allocated; /* Size of the files array. */
-static unsigned num_files; /* Number of used entries in the files array. */
+static unsigned allocated; // Size of the files array.
+static unsigned num_files; // Number of used entries in the files array.
 
 static uint64_t cache_size;
 static size_t files_in_cache;
 static uint64_t cache_size_threshold;
 static size_t files_in_cache_threshold;
 
-/* File comparison function that orders files in mtime order, oldest first. */
+// File comparison function that orders files in mtime order, oldest first.
 static int
 files_compare(struct files **f1, struct files **f2)
 {
@@ -52,33 +43,28 @@ files_compare(struct files **f1, struct files **f2)
        return 1;
 }
 
-/* this builds the list of files in the cache */
+// This builds the list of files in the cache.
 static void
 traverse_fn(const char *fname, struct stat *st)
 {
-       char *p;
-
        if (!S_ISREG(st->st_mode)) {
                return;
        }
 
-       p = basename(fname);
+       char *p = basename(fname);
        if (str_eq(p, "stats")) {
                goto out;
        }
 
        if (str_startswith(p, ".nfs")) {
-               /* Ignore temporary NFS files that may be left for open but deleted
-                * files. */
+               // Ignore temporary NFS files that may be left for open but deleted files.
                goto out;
        }
 
-       if (strstr(p, ".tmp.")) {
-               /* delete any tmp files older than 1 hour */
-               if (st->st_mtime + 3600 < time(NULL)) {
-                       x_unlink(fname);
-                       goto out;
-               }
+       // Delete any tmp files older than 1 hour.
+       if (strstr(p, ".tmp.") && st->st_mtime + 3600 < time(NULL)) {
+               x_unlink(fname);
+               goto out;
        }
 
        if (strstr(p, "CACHEDIR.TAG")) {
@@ -117,9 +103,7 @@ static void
 delete_sibling_file(const char *base, const char *extension)
 {
        struct stat st;
-       char *path;
-
-       path = format("%s%s", base, extension);
+       char *path = format("%s%s", base, extension);
        if (lstat(path, &st) == 0) {
                delete_file(path, file_size(&st));
        } else if (errno != ENOENT && errno != ESTALE) {
@@ -128,21 +112,20 @@ delete_sibling_file(const char *base, const char *extension)
        free(path);
 }
 
-/* sort the files we've found and delete the oldest ones until we are
-   below the thresholds */
-static void
+// Sort the files we've found and delete the oldest ones until we are below the
+// thresholds.
+static bool
 sort_and_clean(void)
 {
-       unsigned i;
-       char *last_base = x_strdup("");
-
        if (num_files > 1) {
-               /* Sort in ascending mtime order. */
+               // Sort in ascending mtime order.
                qsort(files, num_files, sizeof(struct files *), (COMPAR_FN_T)files_compare);
        }
 
-       /* delete enough files to bring us below the threshold */
-       for (i = 0; i < num_files; i++) {
+       // Delete enough files to bring us below the threshold.
+       char *last_base = x_strdup("");
+       bool cleaned = false;
+       for (unsigned i = 0; i < num_files; i++) {
                const char *ext;
 
                if ((cache_size_threshold == 0
@@ -160,56 +143,61 @@ sort_and_clean(void)
                    || str_eq(ext, ".stderr")
                    || str_eq(ext, "")) {
                        char *base = remove_extension(files[i]->fname);
-                       if (!str_eq(base, last_base)) { /* Avoid redundant unlinks. */
-                               /*
-                                * Make sure that all sibling files are deleted so that a cached result
-                                * is removed completely. Note the order of deletions -- the stderr
-                                * file must be deleted last because if the ccache process gets killed
-                                * after deleting the .stderr but before deleting the .o, the cached
-                                * result would be inconsistent.
-                                */
+                       if (!str_eq(base, last_base)) { // Avoid redundant unlinks.
+                               // Make sure that all sibling files are deleted so that a cached result
+                               // is removed completely. Note the order of deletions -- the stderr
+                               // file must be deleted last because if the ccache process gets killed
+                               // after deleting the .stderr but before deleting the .o, the cached
+                               // result would be inconsistent.
                                delete_sibling_file(base, ".o");
                                delete_sibling_file(base, ".d");
                                delete_sibling_file(base, ".gcno");
                                delete_sibling_file(base, ".dia");
                                delete_sibling_file(base, ".stderr");
-                               delete_sibling_file(base, ""); /* Object file from ccache 2.4. */
+                               delete_sibling_file(base, ""); // Object file from ccache 2.4.
                        }
                        free(last_base);
                        last_base = base;
                } else {
-                       /* .manifest or unknown file. */
+                       // .manifest or unknown file.
                        delete_file(files[i]->fname, files[i]->size);
                }
+               cleaned = true;
        }
        free(last_base);
+       return cleaned;
 }
 
-/* cleanup in one cache subdir */
+// Clean up one cache subdirectory.
 void
 cleanup_dir(struct conf *conf, const char *dir)
 {
-       unsigned i;
-
        cc_log("Cleaning up cache directory %s", dir);
 
-       cache_size_threshold = conf->max_size * LIMIT_MULTIPLE / 16;
-       files_in_cache_threshold = conf->max_files * LIMIT_MULTIPLE / 16;
+       // When "max files" or "max cache size" is reached, one of the 16 cache
+       // subdirectories is cleaned up. When doing so, files are deleted (in LRU
+       // order) until the levels are below limit_multiple.
+       cache_size_threshold = conf->max_size * conf->limit_multiple / 16;
+       files_in_cache_threshold = conf->max_files * conf->limit_multiple / 16;
 
        num_files = 0;
        cache_size = 0;
        files_in_cache = 0;
 
-       /* build a list of files */
+       // Build a list of files.
        traverse(dir, traverse_fn);
 
-       /* clean the cache */
-       sort_and_clean();
+       // Clean the cache.
+       bool cleaned = sort_and_clean();
+       if (cleaned) {
+               cc_log("Cleaned up cache directory %s", dir);
+               stats_add_cleanup(dir, 1);
+       }
 
        stats_set_sizes(dir, files_in_cache, cache_size);
 
-       /* free it up */
-       for (i = 0; i < num_files; i++) {
+       // Free it up.
+       for (unsigned i = 0; i < num_files; i++) {
                free(files[i]->fname);
                free(files[i]);
                files[i] = NULL;
@@ -225,48 +213,63 @@ cleanup_dir(struct conf *conf, const char *dir)
        files_in_cache = 0;
 }
 
-/* cleanup in all cache subdirs */
+// Clean up all cache subdirectories.
 void cleanup_all(struct conf *conf)
 {
-       int i;
-
-       for (i = 0; i <= 0xF; i++) {
+       for (int i = 0; i <= 0xF; i++) {
                char *dname = format("%s/%1x", conf->cache_dir, i);
                cleanup_dir(conf, dname);
                free(dname);
        }
 }
 
-/* traverse function for wiping files */
+// Traverse function for wiping files.
 static void wipe_fn(const char *fname, struct stat *st)
 {
-       char *p;
-
        if (!S_ISREG(st->st_mode)) {
                return;
        }
 
-       p = basename(fname);
+       char *p = basename(fname);
        if (str_eq(p, "stats")) {
                free(p);
                return;
        }
        free(p);
 
+       files_in_cache++;
+
        x_unlink(fname);
 }
 
-/* wipe all cached files in all subdirs */
-void wipe_all(struct conf *conf)
+// Wipe one cache subdirectory.
+void
+wipe_dir(struct conf *conf, const char *dir)
 {
-       int i;
+       cc_log("Clearing out cache directory %s", dir);
 
-       for (i = 0; i <= 0xF; i++) {
+       files_in_cache_threshold = conf->max_files * conf->limit_multiple / 16;
+       files_in_cache = 0;
+
+       traverse(dir, wipe_fn);
+
+       if (files_in_cache > 0) {
+               cc_log("Cleared out cache directory %s", dir);
+               stats_add_cleanup(dir, 1);
+       }
+
+       files_in_cache = 0;
+}
+
+// Wipe all cached files in all subdirectories.
+void wipe_all(struct conf *conf)
+{
+       for (int i = 0; i <= 0xF; i++) {
                char *dname = format("%s/%1x", conf->cache_dir, i);
-               traverse(dname, wipe_fn);
+               wipe_dir(conf, dname);
                free(dname);
        }
 
-       /* and fix the counters */
+       // Fix the counters.
        cleanup_all(conf);
 }
index 78d6467..e5bee23 100644 (file)
--- a/compopt.c
+++ b/compopt.c
@@ -1,20 +1,18 @@
-/*
- * Copyright (C) 2010-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 #include "compopt.h"
@@ -36,7 +34,7 @@ static const struct compopt compopts[] = {
        {"--save-temps",    TOO_HARD},
        {"--serialize-diagnostics", TAKES_ARG | TAKES_PATH},
        {"-A",              TAKES_ARG},
-       {"-B",              TAKES_ARG | TAKES_CONCAT_ARG},
+       {"-B",              TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
        {"-D",              AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG},
        {"-E",              TOO_HARD},
        {"-F",              AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
@@ -48,6 +46,7 @@ static const struct compopt compopts[] = {
        {"-MM",             TOO_HARD},
        {"-MQ",             TAKES_ARG},
        {"-MT",             TAKES_ARG},
+       {"-P",              TOO_HARD},
        {"-U",              AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG},
        {"-V",              TAKES_ARG},
        {"-Xassembler",     TAKES_ARG},
@@ -59,30 +58,31 @@ static const struct compopt compopts[] = {
        {"-b",              TAKES_ARG},
        {"-fmodules",       TOO_HARD},
        {"-fno-working-directory", AFFECTS_CPP},
-       {"-fplugin=libcc1plugin", TOO_HARD}, /* interaction with GDB */
+       {"-fplugin=libcc1plugin", TOO_HARD}, // interaction with GDB
        {"-frepo",          TOO_HARD},
        {"-fstack-usage",   TOO_HARD},
        {"-fworking-directory", AFFECTS_CPP},
-       {"-idirafter",      AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
+       {"-idirafter",      AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
        {"-iframework",     AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
-       {"-imacros",        AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-imultilib",      AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-include",        AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-include-pch",    AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-install_name",   TAKES_ARG}, /* Darwin linker option */
-       {"-iprefix",        AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-iquote",         AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-isysroot",       AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-isystem",        AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-iwithprefix",    AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
-       {"-iwithprefixbefore", AFFECTS_CPP | TAKES_ARG | TAKES_PATH},
+       {"-imacros",        AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-imultilib",      AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-include",        AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-include-pch",    AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-install_name",   TAKES_ARG}, // Darwin linker option
+       {"-iprefix",        AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-iquote",         AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-isysroot",       AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-isystem",        AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-iwithprefix",    AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
+       {"-iwithprefixbefore",
+        AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},
        {"-nostdinc",       AFFECTS_CPP},
        {"-nostdinc++",     AFFECTS_CPP},
        {"-remap",          AFFECTS_CPP},
        {"-save-temps",     TOO_HARD},
        {"-stdlib=",        AFFECTS_CPP | TAKES_CONCAT_ARG},
        {"-trigraphs",      AFFECTS_CPP},
-       {"-u",              TAKES_ARG},
+       {"-u",              TAKES_ARG | TAKES_CONCAT_ARG},
 };
 
 
@@ -122,7 +122,7 @@ find_prefix(const char *option)
                 sizeof(compopts[0]), compare_prefix_compopts);
 }
 
-/* Runs fn on the first two characters of option. */
+// Runs fn on the first two characters of option.
 bool
 compopt_short(bool (*fn)(const char *), const char *option)
 {
@@ -132,12 +132,11 @@ compopt_short(bool (*fn)(const char *), const char *option)
        return retval;
 }
 
-/* For test purposes. */
+// For test purposes.
 bool
 compopt_verify_sortedness(void)
 {
-       size_t i;
-       for (i = 1; i < sizeof(compopts)/sizeof(compopts[0]); i++) {
+       for (size_t i = 1; i < sizeof(compopts)/sizeof(compopts[0]); i++) {
                if (strcmp(compopts[i-1].name, compopts[i].name) >= 0) {
                        fprintf(stderr,
                                "compopt_verify_sortedness: %s >= %s\n",
@@ -184,13 +183,19 @@ compopt_takes_arg(const char *option)
        return co && (co->type & TAKES_ARG);
 }
 
-/* Determines if the prefix of the option matches any option and affects the
- * preprocessor.
- */
+bool
+compopt_takes_concat_arg(const char *option)
+{
+       const struct compopt *co = find(option);
+       return co && (co->type & TAKES_CONCAT_ARG);
+}
+
+// Determines if the prefix of the option matches any option and affects the
+// preprocessor.
 bool
 compopt_prefix_affects_cpp(const char *option)
 {
-       /* prefix options have to take concatentated args */
+       // Prefix options have to take concatentated args.
        const struct compopt *co = find_prefix(option);
        return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_CPP);
 }
index 882187d..d72639f 100644 (file)
--- a/compopt.h
+++ b/compopt.h
@@ -9,6 +9,7 @@ bool compopt_too_hard(const char *option);
 bool compopt_too_hard_for_direct_mode(const char *option);
 bool compopt_takes_path(const char *option);
 bool compopt_takes_arg(const char *option);
+bool compopt_takes_concat_arg(const char *option);
 bool compopt_prefix_affects_cpp(const char *option);
 
-#endif /* CCACHE_COMPOPT_H */
+#endif // CCACHE_COMPOPT_H
diff --git a/conf.c b/conf.c
index 0179e2d..cfa2874 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -1,20 +1,18 @@
-/*
- * Copyright (C) 2011-2014 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2011-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "conf.h"
 #include "ccache.h"
@@ -62,11 +60,26 @@ parse_env_string(const char *str, void *result, char **errmsg)
 }
 
 static bool
+parse_float(const char *str, void *result, char **errmsg)
+{
+       float *value = (float *)result;
+       errno = 0;
+       char *endptr;
+       float x = strtof(str, &endptr);
+       if (errno == 0 && *str != '\0' && *endptr == '\0') {
+               *value = x;
+               return true;
+       } else {
+               *errmsg = format("invalid floating point: \"%s\"", str);
+               return false;
+       }
+}
+
+static bool
 parse_size(const char *str, void *result, char **errmsg)
 {
        uint64_t *value = (uint64_t *)result;
        uint64_t size;
-       *errmsg = NULL;
        if (parse_size_with_suffix(str, &size)) {
                *value = size;
                return true;
@@ -80,13 +93,14 @@ static bool
 parse_sloppiness(const char *str, void *result, char **errmsg)
 {
        unsigned *value = (unsigned *)result;
-       char *word, *p, *q, *saveptr = NULL;
-
        if (!str) {
                return *value;
        }
-       p = x_strdup(str);
-       q = p;
+
+       char *p = x_strdup(str);
+       char *q = p;
+       char *word;
+       char *saveptr = NULL;
        while ((word = strtok_r(q, ", ", &saveptr))) {
                if (str_eq(word, "file_macro")) {
                        *value |= SLOPPY_FILE_MACRO;
@@ -96,6 +110,8 @@ parse_sloppiness(const char *str, void *result, char **errmsg)
                        *value |= SLOPPY_INCLUDE_FILE_CTIME;
                } else if (str_eq(word, "include_file_mtime")) {
                        *value |= SLOPPY_INCLUDE_FILE_MTIME;
+               } else if (str_eq(word, "no_system_headers")) {
+                       *value |= SLOPPY_NO_SYSTEM_HEADERS;
                } else if (str_eq(word, "pch_defines")) {
                        *value |= SLOPPY_PCH_DEFINES;
                } else if (str_eq(word, "time_macros")) {
@@ -114,8 +130,9 @@ parse_sloppiness(const char *str, void *result, char **errmsg)
 static bool
 parse_string(const char *str, void *result, char **errmsg)
 {
-       char **value = (char **)result;
        (void)errmsg;
+
+       char **value = (char **)result;
        free(*value);
        *value = x_strdup(str);
        return true;
@@ -125,12 +142,13 @@ static bool
 parse_umask(const char *str, void *result, char **errmsg)
 {
        unsigned *value = (unsigned *)result;
-       char *endptr;
        if (str_eq(str, "")) {
                *value = UINT_MAX;
                return true;
        }
+
        errno = 0;
+       char *endptr;
        *value = strtoul(str, &endptr, 8);
        if (errno == 0 && *str != '\0' && *endptr == '\0') {
                return true;
@@ -144,10 +162,9 @@ static bool
 parse_unsigned(const char *str, void *result, char **errmsg)
 {
        unsigned *value = (unsigned *)result;
-       long x;
-       char *endptr;
        errno = 0;
-       x = strtol(str, &endptr, 10);
+       char *endptr;
+       long x = strtol(str, &endptr, 10);
        if (errno == 0 && x >= 0 && *str != '\0' && *endptr == '\0') {
                *value = x;
                return true;
@@ -169,7 +186,7 @@ verify_absolute_path(void *value, char **errmsg)
        char **path = (char **)value;
        assert(*path);
        if (str_eq(*path, "")) {
-               /* The empty string means "disable" in this case. */
+               // The empty string means "disable" in this case.
                return true;
        } else if (is_absolute_path(*path)) {
                return true;
@@ -217,19 +234,15 @@ handle_conf_setting(struct conf *conf, const char *key, const char *value,
                     char **errmsg, bool from_env_variable, bool negate_boolean,
                     const char *origin)
 {
-       const struct conf_item *item;
-
-       item = find_conf(key);
+       const struct conf_item *item = find_conf(key);
        if (!item) {
                *errmsg = format("unknown configuration option \"%s\"", key);
                return false;
        }
 
        if (from_env_variable && item->parser == parse_bool) {
-               /*
-                * Special rule for boolean settings from the environment: any value means
-                * true.
-                */
+               // Special rule for boolean settings from the environment: any value means
+               // true.
                bool *value = (bool *)((char *)conf + item->offset);
                *value = !negate_boolean;
                goto out;
@@ -250,19 +263,17 @@ out:
 static bool
 parse_line(const char *line, char **key, char **value, char **errmsg)
 {
-       const char *p, *q;
-
 #define SKIP_WS(x) while (isspace(*x)) { ++x; }
 
        *key = NULL;
        *value = NULL;
 
-       p = line;
+       const char *p = line;
        SKIP_WS(p);
        if (*p == '\0' || *p == '#') {
                return true;
        }
-       q = p;
+       const char *q = p;
        while (isalpha(*q) || *q == '_') {
                ++q;
        }
@@ -277,13 +288,13 @@ parse_line(const char *line, char **key, char **value, char **errmsg)
        }
        ++p;
 
-       /* Skip leading whitespace. */
+       // Skip leading whitespace.
        SKIP_WS(p);
        q = p;
        while (*q) {
                ++q;
        }
-       /* Skip trailing whitespace. */
+       // Skip trailing whitespace.
        while (isspace(q[-1])) {
                --q;
        }
@@ -294,11 +305,10 @@ parse_line(const char *line, char **key, char **value, char **errmsg)
 #undef SKIP_WS
 }
 
-/* Create a conf struct with default values. */
+// Create a conf struct with default values.
 struct conf *
 conf_create(void)
 {
-       size_t i;
        struct conf *conf = x_malloc(sizeof(*conf));
        conf->base_dir = x_strdup("");
        conf->cache_dir = format("%s/.ccache", get_home_directory());
@@ -312,23 +322,27 @@ conf_create(void)
        conf->disable = false;
        conf->extra_files_to_hash = x_strdup("");
        conf->hard_link = false;
-       conf->hash_dir = false;
+       conf->hash_dir = true;
+       conf->ignore_headers_in_manifest = x_strdup("");
+       conf->keep_comments_cpp = false;
+       conf->limit_multiple = 0.8f;
        conf->log_file = x_strdup("");
        conf->max_files = 0;
        conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000;
        conf->path = x_strdup("");
        conf->prefix_command = x_strdup("");
+       conf->prefix_command_cpp = x_strdup("");
        conf->read_only = false;
        conf->read_only_direct = false;
        conf->recache = false;
-       conf->run_second_cpp = false;
+       conf->run_second_cpp = true;
        conf->sloppiness = 0;
        conf->stats = true;
        conf->temporary_dir = x_strdup("");
-       conf->umask = UINT_MAX; /* default: don't set umask */
+       conf->umask = UINT_MAX; // Default: don't set umask.
        conf->unify = false;
        conf->item_origins = x_malloc(CONFITEMS_TOTAL_KEYWORDS * sizeof(char *));
-       for (i = 0; i < CONFITEMS_TOTAL_KEYWORDS; ++i) {
+       for (size_t i = 0; i < CONFITEMS_TOTAL_KEYWORDS; ++i) {
                conf->item_origins[i] = "default";
        }
        return conf;
@@ -346,39 +360,40 @@ conf_free(struct conf *conf)
        free(conf->compiler_check);
        free(conf->cpp_extension);
        free(conf->extra_files_to_hash);
+       free(conf->ignore_headers_in_manifest);
        free(conf->log_file);
        free(conf->path);
        free(conf->prefix_command);
+       free(conf->prefix_command_cpp);
        free(conf->temporary_dir);
        free(conf->item_origins);
        free(conf);
 }
 
-/* Note: The path pointer is stored in conf, so path must outlive conf. */
+// Note: The path pointer is stored in conf, so path must outlive conf.
 bool
 conf_read(struct conf *conf, const char *path, char **errmsg)
 {
-       FILE *f;
-       char buf[10000];
-       bool result = true;
-       unsigned line_number;
-
        assert(errmsg);
        *errmsg = NULL;
 
-       f = fopen(path, "r");
+       FILE *f = fopen(path, "r");
        if (!f) {
                *errmsg = format("%s: %s", path, strerror(errno));
                return false;
        }
 
-       line_number = 0;
+       unsigned line_number = 0;
+       bool result = true;
+       char buf[10000];
        while (fgets(buf, sizeof(buf), f)) {
-               char *errmsg2, *key, *value;
-               bool ok;
                ++line_number;
-               ok = parse_line(buf, &key, &value, &errmsg2);
-               if (ok && key) { /* key == NULL if comment or blank line */
+
+               char *key;
+               char *value;
+               char *errmsg2;
+               bool ok = parse_line(buf, &key, &value, &errmsg2);
+               if (ok && key) { // key == NULL if comment or blank line.
                        ok = handle_conf_setting(conf, key, value, &errmsg2, false, false, path);
                }
                free(key);
@@ -403,23 +418,17 @@ out:
 bool
 conf_update_from_environment(struct conf *conf, char **errmsg)
 {
-       char **p;
-       char *q;
-       char *key;
-       char *errmsg2;
-       const struct env_to_conf_item *env_to_conf_item;
-       bool negate;
-       size_t key_start;
-
-       for (p = environ; *p; ++p) {
+       for (char **p = environ; *p; ++p) {
                if (!str_startswith(*p, "CCACHE_")) {
                        continue;
                }
-               q = strchr(*p, '=');
+               char *q = strchr(*p, '=');
                if (!q) {
                        continue;
                }
 
+               bool negate;
+               size_t key_start;
                if (str_startswith(*p + 7, "NO")) {
                        negate = true;
                        key_start = 9;
@@ -427,16 +436,17 @@ conf_update_from_environment(struct conf *conf, char **errmsg)
                        negate = false;
                        key_start = 7;
                }
-               key = x_strndup(*p + key_start, q - *p - key_start);
+               char *key = x_strndup(*p + key_start, q - *p - key_start);
 
-               ++q; /* Now points to the value. */
+               ++q; // Now points to the value.
 
-               env_to_conf_item = find_env_to_conf(key);
+               const struct env_to_conf_item *env_to_conf_item = find_env_to_conf(key);
                if (!env_to_conf_item) {
                        free(key);
                        continue;
                }
 
+               char *errmsg2;
                if (!handle_conf_setting(
                      conf, env_to_conf_item->conf_name, q, &errmsg2, true, negate,
                      "environment")) {
@@ -456,26 +466,20 @@ bool
 conf_set_value_in_file(const char *path, const char *key, const char *value,
                        char **errmsg)
 {
-       FILE *infile, *outfile;
-       char *outpath;
-       char buf[10000];
-       bool found;
-       const struct conf_item *item;
-
-       item = find_conf(key);
+       const struct conf_item *item = find_conf(key);
        if (!item) {
                *errmsg = format("unknown configuration option \"%s\"", key);
                return false;
        }
 
-       infile = fopen(path, "r");
+       FILE *infile = fopen(path, "r");
        if (!infile) {
                *errmsg = format("%s: %s", path, strerror(errno));
                return false;
        }
 
-       outpath = format("%s.tmp", path);
-       outfile = create_tmp_file(&outpath, "w");
+       char *outpath = format("%s.tmp", path);
+       FILE *outfile = create_tmp_file(&outpath, "w");
        if (!outfile) {
                *errmsg = format("%s: %s", outpath, strerror(errno));
                free(outpath);
@@ -483,11 +487,13 @@ conf_set_value_in_file(const char *path, const char *key, const char *value,
                return false;
        }
 
-       found = false;
+       bool found = false;
+       char buf[10000];
        while (fgets(buf, sizeof(buf), infile)) {
-               char *errmsg2, *key2, *value2;
-               bool ok;
-               ok = parse_line(buf, &key2, &value2, &errmsg2);
+               char *key2;
+               char *value2;
+               char *errmsg2;
+               bool ok = parse_line(buf, &key2, &value2, &errmsg2);
                if (ok && key2 && str_eq(key2, key)) {
                        found = true;
                        fprintf(outfile, "%s = %s\n", key, value);
@@ -520,7 +526,6 @@ conf_print_items(struct conf *conf,
                  void *context)
 {
        char *s = x_strdup("");
-       char *s2;
 
        reformat(&s, "base_dir = %s", conf->base_dir);
        printer(s, conf->item_origins[find_conf("base_dir")->number], context);
@@ -564,13 +569,27 @@ conf_print_items(struct conf *conf,
        reformat(&s, "hash_dir = %s", bool_to_string(conf->hash_dir));
        printer(s, conf->item_origins[find_conf("hash_dir")->number], context);
 
+       reformat(&s, "ignore_headers_in_manifest = %s",
+                conf->ignore_headers_in_manifest);
+       printer(s,
+               conf->item_origins[find_conf("ignore_headers_in_manifest")->number],
+               context);
+
+       reformat(&s, "keep_comments_cpp = %s",
+                bool_to_string(conf->keep_comments_cpp));
+       printer(s, conf->item_origins[find_conf(
+                                       "keep_comments_cpp")->number], context);
+
+       reformat(&s, "limit_multiple = %.1f", (double)conf->limit_multiple);
+       printer(s, conf->item_origins[find_conf("limit_multiple")->number], context);
+
        reformat(&s, "log_file = %s", conf->log_file);
        printer(s, conf->item_origins[find_conf("log_file")->number], context);
 
        reformat(&s, "max_files = %u", conf->max_files);
        printer(s, conf->item_origins[find_conf("max_files")->number], context);
 
-       s2 = format_parsable_size_with_suffix(conf->max_size);
+       char *s2 = format_parsable_size_with_suffix(conf->max_size);
        reformat(&s, "max_size = %s", s2);
        printer(s, conf->item_origins[find_conf("max_size")->number], context);
        free(s2);
@@ -581,6 +600,10 @@ conf_print_items(struct conf *conf,
        reformat(&s, "prefix_command = %s", conf->prefix_command);
        printer(s, conf->item_origins[find_conf("prefix_command")->number], context);
 
+       reformat(&s, "prefix_command_cpp = %s", conf->prefix_command_cpp);
+       printer(s, conf->item_origins[find_conf(
+                                       "prefix_command_cpp")->number], context);
+
        reformat(&s, "read_only = %s", bool_to_string(conf->read_only));
        printer(s, conf->item_origins[find_conf("read_only")->number], context);
 
@@ -613,8 +636,11 @@ conf_print_items(struct conf *conf,
        if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) {
                reformat(&s, "%sfile_stat_matches, ", s);
        }
+       if (conf->sloppiness & SLOPPY_NO_SYSTEM_HEADERS) {
+               reformat(&s, "%sno_system_headers, ", s);
+       }
        if (conf->sloppiness) {
-               /* Strip last ", ". */
+               // Strip last ", ".
                s[strlen(s) - 2] = '\0';
        }
        printer(s, conf->item_origins[find_conf("sloppiness")->number], context);
diff --git a/conf.h b/conf.h
index 0e30028..232dcfd 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -17,11 +17,15 @@ struct conf {
        char *extra_files_to_hash;
        bool hard_link;
        bool hash_dir;
+       char *ignore_headers_in_manifest;
+       bool keep_comments_cpp;
+       float limit_multiple;
        char *log_file;
        unsigned max_files;
        uint64_t max_size;
        char *path;
        char *prefix_command;
+       char *prefix_command_cpp;
        bool read_only;
        bool read_only_direct;
        bool recache;
index cc67c93..f990225 100644 (file)
@@ -19,6 +19,9 @@
 /* Define to 1 if your compiler supports extern inline */
 #undef HAVE_EXTERN_INLINE
 
+/* Define to 1 if you have the `GetFinalPathNameByHandleW' function. */
+#undef HAVE_GETFINALPATHNAMEBYHANDLEW
+
 /* Define to 1 if you have the `gethostname' function. */
 #undef HAVE_GETHOSTNAME
 
@@ -73,9 +76,6 @@
 /* Define to 1 if you have the <stdarg.h> header file. */
 #undef HAVE_STDARG_H
 
-/* Define to 1 if stdbool.h conforms to C99. */
-#undef HAVE_STDBOOL_H
-
 /* Define to 1 if you have the <stddef.h> header file. */
 #undef HAVE_STDDEF_H
 
 /* Define to 1 if you have a C99 compliant `vsnprintf' function. */
 #undef HAVE_VSNPRINTF
 
-/* Define to 1 if the system has the type `_Bool'. */
-#undef HAVE__BOOL
-
 /* Define to 1 if you have the `__va_copy' function or macro. */
 #undef HAVE___VA_COPY
 
index ba2e7f3..8195d47 100755 (executable)
--- a/configure
+++ b/configure
@@ -3125,6 +3125,187 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
 ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
+   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
+$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
+if ${ac_cv_prog_cc_c99+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
+
+// Check varargs macros.  These examples are taken from C99 6.10.3.5.
+#define debug(...) fprintf (stderr, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+  int x = 1234;
+  int y = 5678;
+  debug ("Flag");
+  debug ("X = %d\n", x);
+  showlist (The first, second, and third items.);
+  report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+  your preprocessor is broken;
+#endif
+#if BIG_OK
+#else
+  your preprocessor is broken;
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+  int datasize;
+  double data[];
+};
+
+struct named_init {
+  int number;
+  const wchar_t *name;
+  double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+  // See if C++-style comments work.
+  // Iterate through items via the restricted pointer.
+  // Also check for declarations in for loops.
+  for (unsigned int i = 0; *(text+i) != '\0'; ++i)
+    continue;
+  return 0;
+}
+
+// Check varargs and va_copy.
+static void
+test_varargs (const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+  va_list args_copy;
+  va_copy (args_copy, args);
+
+  const char *str;
+  int number;
+  float fnumber;
+
+  while (*format)
+    {
+      switch (*format++)
+       {
+       case 's': // string
+         str = va_arg (args_copy, const char *);
+         break;
+       case 'd': // int
+         number = va_arg (args_copy, int);
+         break;
+       case 'f': // float
+         fnumber = va_arg (args_copy, double);
+         break;
+       default:
+         break;
+       }
+    }
+  va_end (args_copy);
+  va_end (args);
+}
+
+int
+main ()
+{
+
+  // Check bool.
+  _Bool success = false;
+
+  // Check restrict.
+  if (test_restrict ("String literal") == 0)
+    success = true;
+  char *restrict newvar = "Another string";
+
+  // Check varargs.
+  test_varargs ("s, d' f .", "string", 65, 34.234);
+  test_varargs_macros ();
+
+  // Check flexible array members.
+  struct incomplete_array *ia =
+    malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+  ia->datasize = 10;
+  for (int i = 0; i < ia->datasize; ++i)
+    ia->data[i] = i * 1.234;
+
+  // Check named initializers.
+  struct named_init ni = {
+    .number = 34,
+    .name = L"Test wide string",
+    .average = 543.34343,
+  };
+
+  ni.number = 58;
+
+  int dynamic_array[ni.number];
+  dynamic_array[ni.number - 1] = 543;
+
+  // work around unused variable warnings
+  return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
+         || dynamic_array[ni.number - 1] != 543);
+
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c99" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c99"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+$as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c99" != xno; then :
+
+fi
+
+
+if test "$ac_cv_prog_cc_c99" = no; then
+    as_fn_error $? "cannot find a C99-compatible compiler" "$LINENO" 5
+fi
+
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -3904,6 +4085,48 @@ $as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h
 
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if ${ac_cv_header_sys_wait_h+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+  int s;
+  wait (&s);
+  s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_sys_wait_h=yes
+else
+  ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
 $as_echo_n "checking for grep that handles long lines and -e... " >&6; }
@@ -4164,141 +4387,6 @@ fi
 done
 
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5
-$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; }
-if ${ac_cv_header_stdbool_h+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-             #include <stdbool.h>
-             #ifndef bool
-              "error: bool is not defined"
-             #endif
-             #ifndef false
-              "error: false is not defined"
-             #endif
-             #if false
-              "error: false is not 0"
-             #endif
-             #ifndef true
-              "error: true is not defined"
-             #endif
-             #if true != 1
-              "error: true is not 1"
-             #endif
-             #ifndef __bool_true_false_are_defined
-              "error: __bool_true_false_are_defined is not defined"
-             #endif
-
-             struct s { _Bool s: 1; _Bool t; } s;
-
-             char a[true == 1 ? 1 : -1];
-             char b[false == 0 ? 1 : -1];
-             char c[__bool_true_false_are_defined == 1 ? 1 : -1];
-             char d[(bool) 0.5 == true ? 1 : -1];
-             /* See body of main program for 'e'.  */
-             char f[(_Bool) 0.0 == false ? 1 : -1];
-             char g[true];
-             char h[sizeof (_Bool)];
-             char i[sizeof s.t];
-             enum { j = false, k = true, l = false * true, m = true * 256 };
-             /* The following fails for
-                HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
-             _Bool n[m];
-             char o[sizeof n == m * sizeof n[0] ? 1 : -1];
-             char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
-             /* Catch a bug in an HP-UX C compiler.  See
-                http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
-                http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
-              */
-             _Bool q = true;
-             _Bool *pq = &q;
-
-int
-main ()
-{
-
-             bool e = &s;
-             *pq |= q;
-             *pq |= ! q;
-             /* Refer to every declared value, to avoid compiler optimizations.  */
-             return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
-                     + !m + !n + !o + !p + !q + !pq);
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_header_stdbool_h=yes
-else
-  ac_cv_header_stdbool_h=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5
-$as_echo "$ac_cv_header_stdbool_h" >&6; }
-   ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
-if test "x$ac_cv_type__Bool" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE__BOOL 1
-_ACEOF
-
-
-fi
-
-
-if test $ac_cv_header_stdbool_h = yes; then
-
-$as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h
-
-fi
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
-$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
-if ${ac_cv_header_sys_wait_h+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-#include <sys/types.h>
-#include <sys/wait.h>
-#ifndef WEXITSTATUS
-# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
-#endif
-#ifndef WIFEXITED
-# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
-#endif
-
-int
-main ()
-{
-  int s;
-  wait (&s);
-  s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_header_sys_wait_h=yes
-else
-  ac_cv_header_sys_wait_h=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
-$as_echo "$ac_cv_header_sys_wait_h" >&6; }
-if test $ac_cv_header_sys_wait_h = yes; then
-
-$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
-
-fi
-
-
 ac_fn_c_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default"
 if test "x$ac_cv_type_long_long" = xyes; then :
 
@@ -5685,6 +5773,19 @@ fi
 
 if test x${windows_os} = xyes; then
     LIBS="$LIBS -lws2_32"
+    for ac_func in GetFinalPathNameByHandleW
+do :
+  ac_fn_c_check_func "$LINENO" "GetFinalPathNameByHandleW" "ac_cv_func_GetFinalPathNameByHandleW"
+if test "x$ac_cv_func_GetFinalPathNameByHandleW" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GETFINALPATHNAMEBYHANDLEW 1
+_ACEOF
+
+else
+  LIBS="$LIBS -lpsapi"
+fi
+done
+
 fi
 
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
index af56dca..a35fac0 100644 (file)
@@ -21,7 +21,11 @@ AC_SUBST(include_dev_mk)
 AC_SUBST(test_suites)
 
 dnl Checks for programs.
-AC_PROG_CC
+AC_PROG_CC_C99
+if test "$ac_cv_prog_cc_c99" = no; then
+    AC_MSG_ERROR(cannot find a C99-compatible compiler)
+fi
+
 AC_PROG_CPP
 AC_PROG_INSTALL
 AC_PROG_RANLIB
@@ -41,7 +45,6 @@ fi
 
 AC_HEADER_DIRENT
 AC_HEADER_TIME
-AC_HEADER_STDBOOL
 AC_HEADER_SYS_WAIT
 
 AC_CHECK_TYPES(long long)
@@ -123,6 +126,7 @@ fi
 dnl Linking on Windows needs ws2_32
 if test x${windows_os} = xyes; then
     LIBS="$LIBS -lws2_32"
+    AC_CHECK_FUNCS(GetFinalPathNameByHandleW,[],[LIBS="$LIBS -lpsapi"])
 fi
 
 AC_C_BIGENDIAN
index 6fb21ef..531bc92 100644 (file)
@@ -20,17 +20,21 @@ disable,              9, ITEM(disable, bool)
 extra_files_to_hash, 10, ITEM(extra_files_to_hash, env_string)
 hard_link,           11, ITEM(hard_link, bool)
 hash_dir,            12, ITEM(hash_dir, bool)
-log_file,            13, ITEM(log_file, env_string)
-max_files,           14, ITEM(max_files, unsigned)
-max_size,            15, ITEM(max_size, size)
-path,                16, ITEM(path, env_string)
-prefix_command,      17, ITEM(prefix_command, env_string)
-read_only,           18, ITEM(read_only, bool)
-read_only_direct,    19, ITEM(read_only_direct, bool)
-recache,             20, ITEM(recache, bool)
-run_second_cpp,      21, ITEM(run_second_cpp, bool)
-sloppiness,          22, ITEM(sloppiness, sloppiness)
-stats,               23, ITEM(stats, bool)
-temporary_dir,       24, ITEM(temporary_dir, env_string)
-umask,               25, ITEM(umask, umask)
-unify,               26, ITEM(unify, bool)
+ignore_headers_in_manifest, 13, ITEM(ignore_headers_in_manifest, env_string)
+keep_comments_cpp,   14, ITEM(keep_comments_cpp, bool)
+limit_multiple,      15, ITEM(limit_multiple, float)
+log_file,            16, ITEM(log_file, env_string)
+max_files,           17, ITEM(max_files, unsigned)
+max_size,            18, ITEM(max_size, size)
+path,                19, ITEM(path, env_string)
+prefix_command,      20, ITEM(prefix_command, env_string)
+prefix_command_cpp,  21, ITEM(prefix_command_cpp, env_string)
+read_only,           22, ITEM(read_only, bool)
+read_only_direct,    23, ITEM(read_only_direct, bool)
+recache,             24, ITEM(recache, bool)
+run_second_cpp,      25, ITEM(run_second_cpp, bool)
+sloppiness,          26, ITEM(sloppiness, sloppiness)
+stats,               27, ITEM(stats, bool)
+temporary_dir,       28, ITEM(temporary_dir, env_string)
+umask,               29, ITEM(umask, umask)
+unify,               30, ITEM(unify, bool)
index 39e95af..7482557 100644 (file)
@@ -31,7 +31,7 @@
 
 #line 8 "confitems.gperf"
 struct conf_item;
-/* maximum key range = 45, duplicates = 0 */
+/* maximum key range = 46, duplicates = 0 */
 
 #ifdef __GNUC__
 __inline
@@ -54,10 +54,10 @@ confitems_hash (register const char *str, register unsigned int len)
       50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
       50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
       50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
-      50, 50, 50, 50, 50, 50, 50, 20,  5,  0,
-      10,  0, 50, 50, 15,  5, 50, 50, 20, 10,
-       0,  0, 10, 50,  0,  0,  0,  5, 50, 50,
-      30, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+      50, 50, 50, 50, 50, 50, 50,  0, 13,  0,
+       5, 10, 50,  0, 30, 20, 50,  0, 10, 20,
+       5,  0,  0, 50,  5,  0, 10, 15, 50, 50,
+      20, 50, 50, 50, 50, 50, 50, 50, 50, 50,
       50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
       50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
       50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
@@ -87,10 +87,10 @@ confitems_get (register const char *str, register unsigned int len)
 {
   enum
     {
-      TOTAL_KEYWORDS = 27,
+      TOTAL_KEYWORDS = 31,
       MIN_WORD_LENGTH = 4,
-      MAX_WORD_LENGTH = 19,
-      MIN_HASH_VALUE = 5,
+      MAX_WORD_LENGTH = 26,
+      MIN_HASH_VALUE = 4,
       MAX_HASH_VALUE = 49
     };
 
@@ -98,72 +98,77 @@ confitems_get (register const char *str, register unsigned int len)
     {
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+#line 29 "confitems.gperf"
+      {"path",                19, ITEM(path, env_string)},
+      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
       {"",0,NULL,0,NULL},
-#line 33 "confitems.gperf"
-      {"stats",               23, ITEM(stats, bool)},
-      {"",0,NULL,0,NULL},
-#line 30 "confitems.gperf"
-      {"recache",             20, ITEM(recache, bool)},
 #line 13 "confitems.gperf"
       {"compiler",             3, ITEM(compiler, string)},
-#line 28 "confitems.gperf"
-      {"read_only",           18, ITEM(read_only, bool)},
-#line 36 "confitems.gperf"
-      {"unify",               26, ITEM(unify, bool)},
+#line 11 "confitems.gperf"
+      {"cache_dir",            1, ITEM(cache_dir, env_string)},
+      {"",0,NULL,0,NULL},
 #line 15 "confitems.gperf"
       {"compression",          5, ITEM(compression, bool)},
       {"",0,NULL,0,NULL},
-#line 34 "confitems.gperf"
-      {"temporary_dir",       24, ITEM(temporary_dir, env_string)},
+#line 17 "confitems.gperf"
+      {"cpp_extension",        7, ITEM(cpp_extension, string)},
 #line 14 "confitems.gperf"
       {"compiler_check",       4, ITEM(compiler_check, string)},
-      {"",0,NULL,0,NULL},
-#line 29 "confitems.gperf"
-      {"read_only_direct",    19, ITEM(read_only_direct, bool)},
+#line 37 "confitems.gperf"
+      {"stats",               27, ITEM(stats, bool)},
+#line 12 "confitems.gperf"
+      {"cache_dir_levels",     2, ITEM_V(cache_dir_levels, unsigned, dir_levels)},
 #line 16 "confitems.gperf"
       {"compression_level",    6, ITEM(compression_level, unsigned)},
-      {"",0,NULL,0,NULL},
+#line 26 "confitems.gperf"
+      {"log_file",            16, ITEM(log_file, env_string)},
+#line 30 "confitems.gperf"
+      {"prefix_command",      20, ITEM(prefix_command, env_string)},
+#line 36 "confitems.gperf"
+      {"sloppiness",          26, ITEM(sloppiness, sloppiness)},
+#line 10 "confitems.gperf"
+      {"base_dir",             0, ITEM_V(base_dir, env_string, absolute_path)},
+#line 34 "confitems.gperf"
+      {"recache",             24, ITEM(recache, bool)},
 #line 31 "confitems.gperf"
-      {"run_second_cpp",      21, ITEM(run_second_cpp, bool)},
-#line 35 "confitems.gperf"
-      {"umask",               25, ITEM(umask, umask)},
+      {"prefix_command_cpp",  21, ITEM(prefix_command_cpp, env_string)},
+#line 32 "confitems.gperf"
+      {"read_only",           22, ITEM(read_only, bool)},
+#line 40 "confitems.gperf"
+      {"unify",               30, ITEM(unify, bool)},
       {"",0,NULL,0,NULL},
+#line 24 "confitems.gperf"
+      {"keep_comments_cpp",   14, ITEM(keep_comments_cpp, bool)},
+#line 28 "confitems.gperf"
+      {"max_size",            18, ITEM(max_size, size)},
+#line 27 "confitems.gperf"
+      {"max_files",           17, ITEM(max_files, unsigned)},
+      {"",0,NULL,0,NULL},
+#line 33 "confitems.gperf"
+      {"read_only_direct",    23, ITEM(read_only_direct, bool)},
 #line 19 "confitems.gperf"
       {"disable",              9, ITEM(disable, bool)},
-#line 17 "confitems.gperf"
-      {"cpp_extension",        7, ITEM(cpp_extension, string)},
-#line 27 "confitems.gperf"
-      {"prefix_command",      17, ITEM(prefix_command, env_string)},
+#line 38 "confitems.gperf"
+      {"temporary_dir",       28, ITEM(temporary_dir, env_string)},
+#line 35 "confitems.gperf"
+      {"run_second_cpp",      25, ITEM(run_second_cpp, bool)},
       {"",0,NULL,0,NULL},
 #line 18 "confitems.gperf"
       {"direct_mode",          8, ITEM(direct_mode, bool)},
       {"",0,NULL,0,NULL},
-#line 23 "confitems.gperf"
-      {"log_file",            13, ITEM(log_file, env_string)},
-#line 11 "confitems.gperf"
-      {"cache_dir",            1, ITEM(cache_dir, env_string)},
-#line 32 "confitems.gperf"
-      {"sloppiness",          22, ITEM(sloppiness, sloppiness)},
-      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
-#line 10 "confitems.gperf"
-      {"base_dir",             0, ITEM_V(base_dir, env_string, absolute_path)},
-#line 26 "confitems.gperf"
-      {"path",                16, ITEM(path, env_string)},
-      {"",0,NULL,0,NULL},
-#line 12 "confitems.gperf"
-      {"cache_dir_levels",     2, ITEM_V(cache_dir_levels, unsigned, dir_levels)},
-      {"",0,NULL,0,NULL},
-#line 25 "confitems.gperf"
-      {"max_size",            15, ITEM(max_size, size)},
-#line 24 "confitems.gperf"
-      {"max_files",           14, ITEM(max_files, unsigned)},
-      {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
-      {"",0,NULL,0,NULL},
 #line 22 "confitems.gperf"
       {"hash_dir",            12, ITEM(hash_dir, bool)},
 #line 21 "confitems.gperf"
       {"hard_link",           11, ITEM(hard_link, bool)},
+#line 39 "confitems.gperf"
+      {"umask",               29, ITEM(umask, umask)},
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
+      {"",0,NULL,0,NULL},
+#line 25 "confitems.gperf"
+      {"limit_multiple",      15, ITEM(limit_multiple, float)},
+      {"",0,NULL,0,NULL},
+#line 23 "confitems.gperf"
+      {"ignore_headers_in_manifest", 13, ITEM(ignore_headers_in_manifest, env_string)},
       {"",0,NULL,0,NULL}, {"",0,NULL,0,NULL},
 #line 20 "confitems.gperf"
       {"extra_files_to_hash", 10, ITEM(extra_files_to_hash, env_string)}
@@ -183,4 +188,4 @@ confitems_get (register const char *str, register unsigned int len)
     }
   return 0;
 }
-static const size_t CONFITEMS_TOTAL_KEYWORDS = 27;
+static const size_t CONFITEMS_TOTAL_KEYWORDS = 31;
index 4e3036a..1693c97 100644 (file)
@@ -1,29 +1,25 @@
-/*
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-/* A simple array of unsigned integers used for the statistics counters. */
+// A simple array of unsigned integers used for the statistics counters.
 
 #include "ccache.h"
 
-/*
- * Allocate and initialize a struct counters. Data entries up to the size are
- * set to 0.
- */
+// Allocate and initialize a struct counters. Data entries up to the size are
+// set to 0.
 struct counters *
 counters_init(size_t initial_size)
 {
@@ -35,9 +31,7 @@ counters_init(size_t initial_size)
        return c;
 }
 
-/*
- * Free a struct counters.
- */
+// Free a counters struct.
 void
 counters_free(struct counters *c)
 {
@@ -45,16 +39,12 @@ counters_free(struct counters *c)
        free(c);
 }
 
-/*
- * Set a new size. New data entries are set to 0.
- */
+// Set a new size. New data entries are set to 0.
 void
 counters_resize(struct counters *c, size_t new_size)
 {
        if (new_size > c->size) {
-               size_t i;
                bool realloc = false;
-
                while (c->allocated < new_size) {
                        c->allocated += 32 + c->allocated;
                        realloc = true;
@@ -62,7 +52,7 @@ counters_resize(struct counters *c, size_t new_size)
                if (realloc) {
                        c->data = x_realloc(c->data, c->allocated * sizeof(c->data[0]));
                }
-               for (i = c->size; i < new_size; i++) {
+               for (size_t i = c->size; i < new_size; i++) {
                        c->data[i] = 0;
                }
        }
index 4552379..91dd75e 100644 (file)
@@ -1,28 +1,28 @@
-/*
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #ifndef COUNTERS_H
 #define COUNTERS_H
 
+#include <stddef.h>
+
 struct counters {
-       unsigned *data;   /* counter value */
-       size_t size;      /* logical array size */
-       size_t allocated; /* allocated size */
+       unsigned *data;   // counter value
+       size_t size;      // logical array size
+       size_t allocated; // allocated size
 };
 
 struct counters *counters_init(size_t initial_size);
index a6176f1..d141b78 100644 (file)
--- a/dev.mk.in
+++ b/dev.mk.in
@@ -25,9 +25,7 @@ ifneq ($(shell uname), Darwin)
     dist_archives += ccache-$(version).tar.xz
 endif
 
-generated_docs = \
-    ccache.1 AUTHORS.html INSTALL.html LICENSE.html MANUAL.html NEWS.html \
-    README.html
+generated_docs = ccache.1 AUTHORS.html LICENSE.html MANUAL.html NEWS.html
 built_dist_files = $(generated_docs)
 
 headers = \
@@ -50,7 +48,7 @@ headers = \
     test/suites.h \
     test/util.h
 
-files_to_clean += *.tar.bz2 *.tar.gz *.tar.xz *.xml .deps/*
+files_to_clean += *.tar.bz2 *.tar.gz *.tar.xz *.xml .deps/* perfdir.*
 files_to_distclean += $(built_dist_files) version.c test/suites.h
 files_to_distclean += .deps version.c dev.mk
 
@@ -61,12 +59,11 @@ source_dist_files = \
     AUTHORS.txt \
     GPL-3.0.txt \
     HACKING.txt \
-    INSTALL.txt \
     LICENSE.txt \
     MANUAL.txt \
     Makefile.in \
     NEWS.txt \
-    README.txt \
+    README.md \
     autogen.sh \
     config.guess \
     config.h.in \
@@ -113,6 +110,7 @@ $(dist_archives): $(dist_files)
        mkdir $$dir && \
        (cd $(srcdir) && \
         rsync -r --relative $(source_dist_files) $$dir) && \
+       cp $(srcdir)/INSTALL-from-release-archive.md $$dir/INSTALL.md && \
        cp $(built_dist_files) $$dir && \
        echo "Remove this file to enable developer mode." >$$dir/dev_mode_disabled && \
        (cd $$tmpdir && \
index f623c45..81d8444 100644 (file)
@@ -14,6 +14,7 @@ COMPILERCHECK, "compiler_check"
 COMPRESS, "compression"
 COMPRESSLEVEL, "compression_level"
 CPP2, "run_second_cpp"
+COMMENTS, "keep_comments_cpp"
 DIR, "cache_dir"
 DIRECT, "direct_mode"
 DISABLE, "disable"
@@ -21,12 +22,15 @@ EXTENSION, "cpp_extension"
 EXTRAFILES, "extra_files_to_hash"
 HARDLINK, "hard_link"
 HASHDIR, "hash_dir"
+IGNOREHEADERS, "ignore_headers_in_manifest"
+LIMIT_MULTIPLE, "limit_multiple"
 LOGFILE, "log_file"
 MAXFILES, "max_files"
 MAXSIZE, "max_size"
 NLEVELS, "cache_dir_levels"
 PATH, "path"
 PREFIX, "prefix_command"
+PREFIX_CPP, "prefix_command_cpp"
 READONLY, "read_only"
 READONLY_DIRECT, "read_only_direct"
 RECACHE, "recache"
index cb8b53e..1265bd6 100644 (file)
@@ -31,7 +31,7 @@
 
 #line 9 "envtoconfitems.gperf"
 struct env_to_conf_item;
-/* maximum key range = 41, duplicates = 0 */
+/* maximum key range = 42, duplicates = 0 */
 
 #ifdef __GNUC__
 __inline
@@ -45,32 +45,32 @@ envtoconfitems_hash (register const char *str, register unsigned int len)
 {
   static const unsigned char asso_values[] =
     {
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 20,  0,  0, 10,
-      20, 43, 15, 43, 10, 43,  5, 10, 15,  0,
-       5, 10,  5,  0,  0,  0, 43, 43, 43, 43,
-      10, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-      43, 43, 43, 43, 43, 43, 43
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 20,  0,  0, 10,
+       0, 44,  5, 15,  0, 44, 10, 25,  9,  0,
+       5, 10,  5, 15, 10,  5, 44, 44, 44, 44,
+       0, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+      44, 44, 44, 44, 44, 44, 44
     };
   register int hval = len;
 
@@ -101,11 +101,11 @@ envtoconfitems_get (register const char *str, register unsigned int len)
 {
   enum
     {
-      TOTAL_KEYWORDS = 27,
+      TOTAL_KEYWORDS = 31,
       MIN_WORD_LENGTH = 2,
       MAX_WORD_LENGTH = 15,
       MIN_HASH_VALUE = 2,
-      MAX_HASH_VALUE = 42
+      MAX_HASH_VALUE = 43
     };
 
   static const struct env_to_conf_item wordlist[] =
@@ -113,67 +113,76 @@ envtoconfitems_get (register const char *str, register unsigned int len)
       {"",""}, {"",""},
 #line 12 "envtoconfitems.gperf"
       {"CC", "compiler"},
-#line 17 "envtoconfitems.gperf"
+#line 18 "envtoconfitems.gperf"
       {"DIR", "cache_dir"},
 #line 16 "envtoconfitems.gperf"
       {"CPP2", "run_second_cpp"},
-#line 34 "envtoconfitems.gperf"
-      {"STATS", "stats"},
-#line 18 "envtoconfitems.gperf"
-      {"DIRECT", "direct_mode"},
+      {"",""},
 #line 19 "envtoconfitems.gperf"
+      {"DIRECT", "direct_mode"},
+#line 20 "envtoconfitems.gperf"
       {"DISABLE", "disable"},
-#line 14 "envtoconfitems.gperf"
-      {"COMPRESS", "compression"},
-#line 28 "envtoconfitems.gperf"
+#line 17 "envtoconfitems.gperf"
+      {"COMMENTS", "keep_comments_cpp"},
+#line 31 "envtoconfitems.gperf"
       {"PATH", "path"},
-#line 36 "envtoconfitems.gperf"
-      {"UMASK", "umask"},
-      {"",""},
+#line 41 "envtoconfitems.gperf"
+      {"UNIFY", "unify"},
 #line 32 "envtoconfitems.gperf"
+      {"PREFIX", "prefix_command"},
+#line 36 "envtoconfitems.gperf"
       {"RECACHE", "recache"},
-#line 15 "envtoconfitems.gperf"
-      {"COMPRESSLEVEL", "compression_level"},
-      {"",""},
-#line 37 "envtoconfitems.gperf"
-      {"UNIFY", "unify"},
+#line 13 "envtoconfitems.gperf"
+      {"COMPILERCHECK", "compiler_check"},
       {"",""},
-#line 35 "envtoconfitems.gperf"
-      {"TEMPDIR", "temporary_dir"},
+#line 33 "envtoconfitems.gperf"
+      {"PREFIX_CPP", "prefix_command_cpp"},
 #line 30 "envtoconfitems.gperf"
+      {"NLEVELS", "cache_dir_levels"},
+#line 27 "envtoconfitems.gperf"
+      {"LOGFILE", "log_file"},
+#line 34 "envtoconfitems.gperf"
       {"READONLY", "read_only"},
-#line 20 "envtoconfitems.gperf"
+#line 21 "envtoconfitems.gperf"
       {"EXTENSION", "cpp_extension"},
-#line 33 "envtoconfitems.gperf"
-      {"SLOPPINESS", "sloppiness"},
-#line 29 "envtoconfitems.gperf"
-      {"PREFIX", "prefix_command"},
+#line 40 "envtoconfitems.gperf"
+      {"UMASK", "umask"},
+      {"",""},
 #line 24 "envtoconfitems.gperf"
-      {"LOGFILE", "log_file"},
-#line 13 "envtoconfitems.gperf"
-      {"COMPILERCHECK", "compiler_check"},
+      {"HASHDIR", "hash_dir"},
+#line 14 "envtoconfitems.gperf"
+      {"COMPRESS", "compression"},
       {"",""},
-#line 31 "envtoconfitems.gperf"
+#line 35 "envtoconfitems.gperf"
       {"READONLY_DIRECT", "read_only_direct"},
       {"",""},
+#line 39 "envtoconfitems.gperf"
+      {"TEMPDIR", "temporary_dir"},
+#line 15 "envtoconfitems.gperf"
+      {"COMPRESSLEVEL", "compression_level"},
 #line 26 "envtoconfitems.gperf"
+      {"LIMIT_MULTIPLE", "limit_multiple"},
+#line 38 "envtoconfitems.gperf"
+      {"STATS", "stats"},
+      {"",""},
+#line 29 "envtoconfitems.gperf"
       {"MAXSIZE", "max_size"},
-#line 25 "envtoconfitems.gperf"
+#line 28 "envtoconfitems.gperf"
       {"MAXFILES", "max_files"},
-      {"",""}, {"",""}, {"",""},
-#line 23 "envtoconfitems.gperf"
-      {"HASHDIR", "hash_dir"},
-#line 22 "envtoconfitems.gperf"
-      {"HARDLINK", "hard_link"},
-      {"",""}, {"",""}, {"",""},
+      {"",""},
+#line 37 "envtoconfitems.gperf"
+      {"SLOPPINESS", "sloppiness"},
+      {"",""},
 #line 11 "envtoconfitems.gperf"
       {"BASEDIR", "base_dir"},
-      {"",""}, {"",""},
-#line 21 "envtoconfitems.gperf"
-      {"EXTRAFILES", "extra_files_to_hash"},
+#line 23 "envtoconfitems.gperf"
+      {"HARDLINK", "hard_link"},
       {"",""},
-#line 27 "envtoconfitems.gperf"
-      {"NLEVELS", "cache_dir_levels"}
+#line 22 "envtoconfitems.gperf"
+      {"EXTRAFILES", "extra_files_to_hash"},
+      {"",""}, {"",""},
+#line 25 "envtoconfitems.gperf"
+      {"IGNOREHEADERS", "ignore_headers_in_manifest"}
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -190,4 +199,4 @@ envtoconfitems_get (register const char *str, register unsigned int len)
     }
   return 0;
 }
-static const size_t ENVTOCONFITEMS_TOTAL_KEYWORDS = 27;
+static const size_t ENVTOCONFITEMS_TOTAL_KEYWORDS = 31;
index 8681d5f..566ef9e 100644 (file)
--- a/execute.c
+++ b/execute.c
@@ -1,21 +1,19 @@
-/*
- * Copyright (C) Andrew Tridgell 2002
- * Copyright (C) Joel Rosdahl 2011
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2002 Andrew Tridgell
+// Copyright (C) 2011-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
@@ -25,24 +23,17 @@ static char *
 find_executable_in_path(const char *name, const char *exclude_name, char *path);
 
 #ifdef _WIN32
-/*
- * Re-create a win32 command line string based on **argv.
- * http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
- */
+// Re-create a win32 command line string based on **argv.
+// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
 char *
 win32argvtos(char *prefix, char **argv)
 {
-       char *arg;
-       char *ptr;
-       char *str;
-       int l = 0;
-       int i, j;
-
-       i = 0;
-       arg = prefix ? prefix : argv[i++];
+       int i = 0;
+       int k = 0;
+       char *arg = prefix ? prefix : argv[i++];
        do {
                int bs = 0;
-               for (j = 0; arg[j]; j++) {
+               for (int j = 0; arg[j]; j++) {
                        switch (arg[j]) {
                        case '\\':
                                bs++;
@@ -50,23 +41,25 @@ win32argvtos(char *prefix, char **argv)
                        case '"':
                                bs = (bs << 1) + 1;
                        default:
-                               l += bs + 1;
+                               k += bs + 1;
                                bs = 0;
                        }
                }
-               l += (bs << 1) + 3;
+               k += (bs << 1) + 3;
        } while ((arg = argv[i++]));
 
-       str = ptr = malloc(l + 1);
-       if (!str)
+       char *ptr = malloc(k + 1);
+       char *str = ptr;
+       if (!str) {
                return NULL;
+       }
 
        i = 0;
        arg = prefix ? prefix : argv[i++];
        do {
                int bs = 0;
                *ptr++ = '"';
-               for (j = 0; arg[j]; j++) {
+               for (int j = 0; arg[j]; j++) {
                        switch (arg[j]) {
                        case '\\':
                                bs++;
@@ -74,17 +67,19 @@ win32argvtos(char *prefix, char **argv)
                        case '"':
                                bs = (bs << 1) + 1;
                        default:
-                               while (bs && bs--)
+                               while (bs && bs--) {
                                        *ptr++ = '\\';
+                               }
                                *ptr++ = arg[j];
                        }
                }
                bs <<= 1;
-               while (bs && bs--)
+               while (bs && bs--) {
                        *ptr++ = '\\';
+               }
                *ptr++ = '"';
                *ptr++ = ' ';
-               /* cppcheck-suppress unreadVariable */
+               // cppcheck-suppress unreadVariable
        } while ((arg = argv[i++]));
        ptr[-1] = '\0';
 
@@ -96,21 +91,20 @@ win32getshell(char *path)
 {
        char *path_env;
        char *sh = NULL;
-       const char *ext;
-
-       ext = get_extension(path);
-       if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH")))
+       const char *ext = get_extension(path);
+       if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH"))) {
                sh = find_executable_in_path("sh.exe", NULL, path_env);
+       }
        if (!sh && getenv("CCACHE_DETECT_SHEBANG")) {
-               /* Detect shebang. */
-               FILE *fp;
-               fp = fopen(path, "r");
+               // Detect shebang.
+               FILE *fp = fopen(path, "r");
                if (fp) {
                        char buf[10];
                        fgets(buf, sizeof(buf), fp);
                        buf[9] = 0;
-                       if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH")))
+                       if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH"))) {
                                sh = find_executable_in_path("sh.exe", NULL, path_env);
+                       }
                        fclose(fp);
                }
        }
@@ -135,18 +129,15 @@ win32execute(char *path, char **argv, int doreturn,
              int fd_stdout, int fd_stderr)
 {
        PROCESS_INFORMATION pi;
-       STARTUPINFO si;
-       BOOL ret;
-       DWORD exitcode;
-       char *sh = NULL;
-       char *args;
-
        memset(&pi, 0x00, sizeof(pi));
+
+       STARTUPINFO si;
        memset(&si, 0x00, sizeof(si));
 
-       sh = win32getshell(path);
-       if (sh)
+       char *sh = win32getshell(path);
+       if (sh) {
                path = sh;
+       }
 
        si.cb = sizeof(STARTUPINFO);
        if (fd_stdout != -1) {
@@ -159,7 +150,7 @@ win32execute(char *path, char **argv, int doreturn,
                        return -1;
                }
        } else {
-               /* redirect subprocess stdout, stderr into current process */
+               // Redirect subprocess stdout, stderr into current process.
                si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
                si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
                si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
@@ -169,13 +160,14 @@ win32execute(char *path, char **argv, int doreturn,
                        return -1;
                }
        }
-       args = win32argvtos(sh, argv);
 
+       char *args = win32argvtos(sh, argv);
        const char *ext = strrchr(path, '.');
        char full_path_win_ext[MAX_PATH] = {0};
        add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path);
-       ret = CreateProcess(full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL,
-                           &si, &pi);
+       BOOL ret =
+         CreateProcess(full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL,
+                       &si, &pi);
        if (fd_stdout != -1) {
                close(fd_stdout);
                close(fd_stderr);
@@ -183,9 +175,7 @@ win32execute(char *path, char **argv, int doreturn,
        free(args);
        if (ret == 0) {
                LPVOID lpMsgBuf;
-               LPVOID lpDisplayBuf;
                DWORD dw = GetLastError();
-
                FormatMessage(
                  FORMAT_MESSAGE_ALLOCATE_BUFFER |
                  FORMAT_MESSAGE_FROM_SYSTEM |
@@ -193,7 +183,7 @@ win32execute(char *path, char **argv, int doreturn,
                  NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,
                  0, NULL);
 
-               lpDisplayBuf =
+               LPVOID lpDisplayBuf =
                  (LPVOID) LocalAlloc(LMEM_ZEROINIT,
                                      (lstrlen((LPCTSTR) lpMsgBuf)
                                       + lstrlen((LPCTSTR) __FILE__) + 200)
@@ -211,23 +201,24 @@ win32execute(char *path, char **argv, int doreturn,
                return -1;
        }
        WaitForSingleObject(pi.hProcess, INFINITE);
+
+       DWORD exitcode;
        GetExitCodeProcess(pi.hProcess, &exitcode);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
-       if (!doreturn)
+       if (!doreturn) {
                x_exit(exitcode);
+       }
        return exitcode;
 }
 
 #else
 
-/* Execute a compiler backend, capturing all output to the given paths the full
- * path to the compiler to run is in argv[0]. */
+// Execute a compiler backend, capturing all output to the given paths the full
+// path to the compiler to run is in argv[0].
 int
 execute(char **argv, int fd_out, int fd_err, pid_t *pid)
 {
-       int status;
-
        cc_log_argv("Executing ", argv);
 
        block_signals();
@@ -239,7 +230,7 @@ execute(char **argv, int fd_out, int fd_err, pid_t *pid)
        }
 
        if (*pid == 0) {
-               /* Child. */
+               // Child.
                dup2(fd_out, 1);
                close(fd_out);
                dup2(fd_err, 2);
@@ -250,6 +241,7 @@ execute(char **argv, int fd_out, int fd_err, pid_t *pid)
        close(fd_out);
        close(fd_err);
 
+       int status;
        if (waitpid(*pid, &status, 0) != *pid) {
                fatal("waitpid failed: %s", strerror(errno));
        }
@@ -266,21 +258,16 @@ execute(char **argv, int fd_out, int fd_err, pid_t *pid)
 }
 #endif
 
-
-/*
- * Find an executable by name in $PATH. Exclude any that are links to
- * exclude_name.
- */
+// Find an executable by name in $PATH. Exclude any that are links to
+// exclude_name.
 char *
 find_executable(const char *name, const char *exclude_name)
 {
-       char *path;
-
        if (is_absolute_path(name)) {
                return x_strdup(name);
        }
 
-       path = conf->path;
+       char *path = conf->path;
        if (str_eq(path, "")) {
                path = getenv("PATH");
        }
@@ -295,23 +282,20 @@ find_executable(const char *name, const char *exclude_name)
 static char *
 find_executable_in_path(const char *name, const char *exclude_name, char *path)
 {
-       char *tok, *saveptr = NULL;
-
        path = x_strdup(path);
 
-       /* search the path looking for the first compiler of the right name
-          that isn't us */
-       for (tok = strtok_r(path, PATH_DELIM, &saveptr);
+       // Search the path looking for the first compiler of the right name that
+       // isn't us.
+       char *saveptr = NULL;
+       for (char *tok = strtok_r(path, PATH_DELIM, &saveptr);
             tok;
             tok = strtok_r(NULL, PATH_DELIM, &saveptr)) {
 #ifdef _WIN32
                char namebuf[MAX_PATH];
-               int ret = SearchPath(tok, name, NULL,
-                                    sizeof(namebuf), namebuf, NULL);
+               int ret = SearchPath(tok, name, NULL, sizeof(namebuf), namebuf, NULL);
                if (!ret) {
                        char *exename = format("%s.exe", name);
-                       ret = SearchPath(tok, exename, NULL,
-                                        sizeof(namebuf), namebuf, NULL);
+                       ret = SearchPath(tok, exename, NULL, sizeof(namebuf), namebuf, NULL);
                        free(exename);
                }
                (void) exclude_name;
@@ -322,7 +306,7 @@ find_executable_in_path(const char *name, const char *exclude_name, char *path)
 #else
                struct stat st1, st2;
                char *fname = format("%s/%s", tok, name);
-               /* look for a normal executable file */
+               // Look for a normal executable file.
                if (access(fname, X_OK) == 0 &&
                    lstat(fname, &st1) == 0 &&
                    stat(fname, &st2) == 0 &&
@@ -332,7 +316,7 @@ find_executable_in_path(const char *name, const char *exclude_name, char *path)
                                if (buf) {
                                        char *p = basename(buf);
                                        if (str_eq(p, exclude_name)) {
-                                               /* It's a link to "ccache"! */
+                                               // It's a link to "ccache"!
                                                free(p);
                                                free(buf);
                                                continue;
@@ -342,7 +326,7 @@ find_executable_in_path(const char *name, const char *exclude_name, char *path)
                                }
                        }
 
-                       /* Found it! */
+                       // Found it!
                        free(path);
                        return fname;
                }
@@ -357,8 +341,7 @@ find_executable_in_path(const char *name, const char *exclude_name, char *path)
 void
 print_command(FILE *fp, char **argv)
 {
-       int i;
-       for (i = 0; argv[i]; i++) {
+       for (int i = 0; argv[i]; i++) {
                fprintf(fp, "%s%s",  (i == 0) ? "" : " ", argv[i]);
        }
        fprintf(fp, "\n");
index f59617a..5c2c482 100644 (file)
--- a/exitfn.c
+++ b/exitfn.c
@@ -1,20 +1,18 @@
-/*
- * Copyright (C) 2010-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
@@ -38,9 +36,7 @@ call_nullary_exit_function(void *context)
        free(p);
 }
 
-/*
- * Initialize exit functions. Must be called once before exitfn_add* are used.
- */
+// Initialize exit functions. Must be called once before exitfn_add* are used.
 void
 exitfn_init(void)
 {
@@ -49,10 +45,8 @@ exitfn_init(void)
        }
 }
 
-/*
- * Add a nullary function to be called when ccache exits. Functions are called
- * in reverse order.
- */
+// Add a nullary function to be called when ccache exits. Functions are called
+// in reverse order.
 void
 exitfn_add_nullary(void (*function)(void))
 {
@@ -61,34 +55,27 @@ exitfn_add_nullary(void (*function)(void))
        exitfn_add(call_nullary_exit_function, p);
 }
 
-/*
- * Add a function to be called with a context parameter when ccache exits.
- * Functions are called in reverse order.
- */
+// Add a function to be called with a context parameter when ccache exits.
+// Functions are called in reverse order.
 void
 exitfn_add(void (*function)(void *), void *context)
 {
-       struct exit_function *p;
-
-       p = x_malloc(sizeof(*p));
+       struct exit_function *p = x_malloc(sizeof(*p));
        p->function = function;
        p->context = context;
        p->next = exit_functions;
        exit_functions = p;
 }
 
-/*
- * Call added functions.
- */
+// Call added functions.
 void
 exitfn_call(void)
 {
        struct exit_function *p = exit_functions;
        exit_functions = NULL;
        while (p) {
-               struct exit_function *q;
                p->function(p->context);
-               q = p;
+               struct exit_function *q = p;
                p = p->next;
                free(q);
        }
diff --git a/hash.c b/hash.c
index 6f23bb9..3b462ca 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -1,21 +1,19 @@
-/*
- * Copyright (C) 2002 Andrew Tridgell
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2002 Andrew Tridgell
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
@@ -33,7 +31,7 @@ hash_buffer(struct mdfour *md, const void *s, size_t len)
        mdfour_update(md, (unsigned char *)s, len);
 }
 
-/* Return the hash result as a hex string. Caller frees. */
+// Return the hash result as a hex string. Caller frees.
 char *
 hash_result(struct mdfour *md)
 {
@@ -43,7 +41,7 @@ hash_result(struct mdfour *md)
        return format_hash_as_string(sum, (unsigned) md->totalN);
 }
 
-/* return the hash result as 16 binary bytes */
+// Return the hash result as 16 binary bytes.
 void
 hash_result_as_bytes(struct mdfour *md, unsigned char *out)
 {
@@ -54,27 +52,26 @@ hash_result_as_bytes(struct mdfour *md, unsigned char *out)
 bool
 hash_equal(struct mdfour *md1, struct mdfour *md2)
 {
-       unsigned char sum1[16], sum2[16];
+       unsigned char sum1[16];
        hash_result_as_bytes(md1, sum1);
+       unsigned char sum2[16];
        hash_result_as_bytes(md2, sum2);
        return memcmp(sum1, sum2, sizeof(sum1)) == 0;
 }
 
-/*
- * Hash some data that is unlikely to occur in the input. The idea is twofold:
- *
- * - Delimit things like arguments from each other (e.g., so that -I -O2 and
- *   -I-O2 hash differently).
- * - Tag different types of hashed information so that it's possible to do
- *   conditional hashing of information in a safe way (e.g., if we want to hash
- *   information X if CCACHE_A is set and information Y if CCACHE_B is set,
- *   there should never be a hash collision risk).
- */
+// Hash some data that is unlikely to occur in the input. The idea is twofold:
+//
+// - Delimit things like arguments from each other (e.g., so that -I -O2 and
+//   -I-O2 hash differently).
+// - Tag different types of hashed information so that it's possible to do
+//   conditional hashing of information in a safe way (e.g., if we want to hash
+//   information X if CCACHE_A is set and information Y if CCACHE_B is set,
+//   there should never be a hash collision risk).
 void
 hash_delimiter(struct mdfour *md, const char *type)
 {
        hash_buffer(md, HASH_DELIMITER, sizeof(HASH_DELIMITER));
-       hash_buffer(md, type, strlen(type) + 1); /* Include NUL. */
+       hash_buffer(md, type, strlen(type) + 1); // Include NUL.
 }
 
 void
@@ -95,14 +92,12 @@ hash_int(struct mdfour *md, int x)
        hash_buffer(md, (char *)&x, sizeof(x));
 }
 
-/*
- * Add contents of an open file to the hash. Returns true on success, otherwise
- * false.
- */
+// Add contents of an open file to the hash. Returns true on success, otherwise
+// false.
 bool
 hash_fd(struct mdfour *md, int fd)
 {
-       char buf[16384];
+       char buf[READ_BUFFER_SIZE];
        ssize_t n;
 
        while ((n = read(fd, buf, sizeof(buf))) != 0) {
@@ -116,23 +111,18 @@ hash_fd(struct mdfour *md, int fd)
        return n == 0;
 }
 
-/*
- * Add contents of a file to the hash. Returns true on success, otherwise
- * false.
- */
+// Add contents of a file to the hash. Returns true on success, otherwise
+// false.
 bool
 hash_file(struct mdfour *md, const char *fname)
 {
-       int fd;
-       bool ret;
-
-       fd = open(fname, O_RDONLY|O_BINARY);
+       int fd = open(fname, O_RDONLY|O_BINARY);
        if (fd == -1) {
                cc_log("Failed to open %s: %s", fname, strerror(errno));
                return false;
        }
 
-       ret = hash_fd(md, fd);
+       bool ret = hash_fd(md, fd);
        close(fd);
        return ret;
 }
index 6c4b279..ddbd0da 100644 (file)
@@ -1,20 +1,18 @@
-/*
- * Copyright (C) 2009-2015 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 #include "hashutil.h"
@@ -46,39 +44,31 @@ file_hashes_equal(struct file_hash *fh1, struct file_hash *fh2)
               && fh1->size == fh2->size;
 }
 
-/*
- * Search for the strings "__DATE__" and "__TIME__" in str.
- *
- * Returns a bitmask with HASH_SOURCE_CODE_FOUND_DATE and
- * HASH_SOURCE_CODE_FOUND_TIME set appropriately.
- */
+// Search for the strings "__DATE__" and "__TIME__" in str.
+//
+// Returns a bitmask with HASH_SOURCE_CODE_FOUND_DATE and
+// HASH_SOURCE_CODE_FOUND_TIME set appropriately.
 int
 check_for_temporal_macros(const char *str, size_t len)
 {
        int result = 0;
 
-       /*
-        * We're using the Boyer-Moore-Horspool algorithm, which searches starting
-        * from the *end* of the needle. Our needles are 8 characters long, so i
-        * starts at 7.
-        */
+       // We're using the Boyer-Moore-Horspool algorithm, which searches starting
+       // from the *end* of the needle. Our needles are 8 characters long, so i
+       // starts at 7.
        size_t i = 7;
 
        while (i < len) {
-               /*
-                * Check whether the substring ending at str[i] has the form "__...E__". On
-                * the assumption that 'E' is less common in source than '_', we check
-                * str[i-2] first.
-                */
+               // Check whether the substring ending at str[i] has the form "__...E__". On
+               // the assumption that 'E' is less common in source than '_', we check
+               // str[i-2] first.
                if (str[i - 2] == 'E' &&
                    str[i - 0] == '_' &&
                    str[i - 7] == '_' &&
                    str[i - 1] == '_' &&
                    str[i - 6] == '_') {
-                       /*
-                        * Check the remaining characters to see if the substring is "__DATE__"
-                        * or "__TIME__".
-                        */
+                       // Check the remaining characters to see if the substring is "__DATE__"
+                       // or "__TIME__".
                        if (str[i - 5] == 'D' && str[i - 4] == 'A' &&
                            str[i - 3] == 'T') {
                                result |= HASH_SOURCE_CODE_FOUND_DATE;
@@ -88,19 +78,15 @@ check_for_temporal_macros(const char *str, size_t len)
                        }
                }
 
-               /*
-                * macro_skip tells us how far we can skip forward upon seeing str[i] at
-                * the end of a substring.
-                */
+               // macro_skip tells us how far we can skip forward upon seeing str[i] at
+               // the end of a substring.
                i += macro_skip[(uint8_t)str[i]];
        }
 
        return result;
 }
 
-/*
- * Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results.
- */
+// Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results.
 int
 hash_source_code_string(
   struct conf *conf, struct mdfour *hash, const char *str, size_t len,
@@ -108,24 +94,18 @@ hash_source_code_string(
 {
        int result = HASH_SOURCE_CODE_OK;
 
-       /*
-        * Check for __DATE__ and __TIME__ if the sloppiness configuration tells us
-        * we should.
-        */
+       // Check for __DATE__ and __TIME__ if the sloppiness configuration tells us
+       // we should.
        if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) {
                result |= check_for_temporal_macros(str, len);
        }
 
-       /*
-        * Hash the source string.
-        */
+       // Hash the source string.
        hash_buffer(hash, str, len);
 
        if (result & HASH_SOURCE_CODE_FOUND_DATE) {
-               /*
-                * Make sure that the hash sum changes if the (potential) expansion of
-                * __DATE__ changes.
-                */
+               // Make sure that the hash sum changes if the (potential) expansion of
+               // __DATE__ changes.
                time_t t = time(NULL);
                struct tm *now = localtime(&t);
                cc_log("Found __DATE__ in %s", path);
@@ -135,30 +115,23 @@ hash_source_code_string(
                hash_buffer(hash, &now->tm_mday, sizeof(now->tm_mday));
        }
        if (result & HASH_SOURCE_CODE_FOUND_TIME) {
-               /*
-                * We don't know for sure that the program actually uses the __TIME__
-                * macro, but we have to assume it anyway and hash the time stamp. However,
-                * that's not very useful since the chance that we get a cache hit later
-                * the same second should be quite slim... So, just signal back to the
-                * caller that __TIME__ has been found so that the direct mode can be
-                * disabled.
-                */
+               // We don't know for sure that the program actually uses the __TIME__
+               // macro, but we have to assume it anyway and hash the time stamp. However,
+               // that's not very useful since the chance that we get a cache hit later
+               // the same second should be quite slim... So, just signal back to the
+               // caller that __TIME__ has been found so that the direct mode can be
+               // disabled.
                cc_log("Found __TIME__ in %s", path);
        }
 
        return result;
 }
 
-/*
- * Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
- * results.
- */
+// Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
+// results.
 int
 hash_source_code_file(struct conf *conf, struct mdfour *hash, const char *path)
 {
-       char *data;
-       size_t size;
-
        if (is_precompiled_header(path)) {
                if (hash_file(hash, path)) {
                        return HASH_SOURCE_CODE_OK;
@@ -166,12 +139,12 @@ hash_source_code_file(struct conf *conf, struct mdfour *hash, const char *path)
                        return HASH_SOURCE_CODE_ERROR;
                }
        } else {
-               int result;
-
+               char *data;
+               size_t size;
                if (!read_file(path, 0, &data, &size)) {
                        return HASH_SOURCE_CODE_ERROR;
                }
-               result = hash_source_code_string(conf, hash, data, size, path);
+               int result = hash_source_code_string(conf, hash, data, size, path);
                free(data);
                return result;
        }
@@ -182,25 +155,28 @@ hash_command_output(struct mdfour *hash, const char *command,
                     const char *compiler)
 {
 #ifdef _WIN32
-       SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
-       HANDLE pipe_out[2];
-       PROCESS_INFORMATION pi;
-       STARTUPINFO si;
-       DWORD exitcode;
-       char *sh = NULL;
-       char *win32args;
-       char *path;
-       BOOL ret;
-       bool ok;
-       int fd;
-#else
-       pid_t pid;
-       int pipefd[2];
+       // Trim leading space.
+       while (isspace(*command)) {
+               command++;
+       }
+
+       // Add "echo" command.
+       bool cmd;
+       if (str_startswith(command, "echo")) {
+               command = format("cmd.exe /c \"%s\"", command);
+               cmd = true;
+       } else if (str_startswith(command,
+                                 "%compiler%") && str_eq(compiler, "echo")) {
+               command = format("cmd.exe /c \"%s%s\"", compiler, command + 10);
+               cmd = true;
+       } else {
+               command = x_strdup(command);
+               cmd = false;
+       }
 #endif
 
        struct args *args = args_init_from_string(command);
-       int i;
-       for (i = 0; i < args->argc; i++) {
+       for (int i = 0; i < args->argc; i++) {
                if (str_eq(args->argv[i], "%compiler%")) {
                        args_set(args, i, compiler);
                }
@@ -208,39 +184,57 @@ hash_command_output(struct mdfour *hash, const char *command,
        cc_log_argv("Executing compiler check command ", args->argv);
 
 #ifdef _WIN32
+       PROCESS_INFORMATION pi;
        memset(&pi, 0x00, sizeof(pi));
+       STARTUPINFO si;
        memset(&si, 0x00, sizeof(si));
 
-       path = find_executable(args->argv[0], NULL);
-       if (!path)
+       char *path = find_executable(args->argv[0], NULL);
+       if (!path) {
                path = args->argv[0];
-       sh = win32getshell(path);
-       if (sh)
+       }
+       char *sh = win32getshell(path);
+       if (sh) {
                path = sh;
+       }
 
        si.cb = sizeof(STARTUPINFO);
+
+       HANDLE pipe_out[2];
+       SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
        CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0);
        SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0);
        si.hStdOutput = pipe_out[1];
        si.hStdError = pipe_out[1];
        si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
        si.dwFlags = STARTF_USESTDHANDLES;
-       win32args = win32argvtos(sh, args->argv);
-       ret = CreateProcess(path, win32args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);
+
+       char *win32args;
+       if (!cmd) {
+               win32args = win32argvtos(sh, args->argv);
+       } else {
+               win32args = (char *)command;  // quoted
+       }
+       BOOL ret =
+         CreateProcess(path, win32args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);
        CloseHandle(pipe_out[1]);
        args_free(args);
        free(win32args);
+       if (cmd) {
+               free((char *)command);  // Original argument was replaced above.
+       }
        if (ret == 0) {
                stats_update(STATS_COMPCHECK);
                return false;
        }
-       fd = _open_osfhandle((intptr_t) pipe_out[0], O_BINARY);
-       ok = hash_fd(hash, fd);
+       int fd = _open_osfhandle((intptr_t) pipe_out[0], O_BINARY);
+       bool ok = hash_fd(hash, fd);
        if (!ok) {
                cc_log("Error hashing compiler check command output: %s", strerror(errno));
                stats_update(STATS_COMPCHECK);
        }
        WaitForSingleObject(pi.hProcess, INFINITE);
+       DWORD exitcode;
        GetExitCodeProcess(pi.hProcess, &exitcode);
        CloseHandle(pipe_out[0]);
        CloseHandle(pi.hProcess);
@@ -252,34 +246,36 @@ hash_command_output(struct mdfour *hash, const char *command,
        }
        return ok;
 #else
+       int pipefd[2];
        if (pipe(pipefd) == -1) {
                fatal("pipe failed");
        }
-       pid = fork();
+
+       pid_t pid = fork();
        if (pid == -1) {
                fatal("fork failed");
        }
 
        if (pid == 0) {
-               /* Child. */
+               // Child.
                close(pipefd[0]);
                close(0);
                dup2(pipefd[1], 1);
                dup2(pipefd[1], 2);
                _exit(execvp(args->argv[0], args->argv));
-               return false; /* Never reached. */
+               return false; // Never reached.
        } else {
-               /* Parent. */
-               int status;
-               bool ok;
+               // Parent.
                args_free(args);
                close(pipefd[1]);
-               ok = hash_fd(hash, pipefd[0]);
+               bool ok = hash_fd(hash, pipefd[0]);
                if (!ok) {
                        cc_log("Error hashing compiler check command output: %s", strerror(errno));
                        stats_update(STATS_COMPCHECK);
                }
                close(pipefd[0]);
+
+               int status;
                if (waitpid(pid, &status, 0) != pid) {
                        cc_log("waitpid failed");
                        return false;
@@ -298,11 +294,11 @@ bool
 hash_multicommand_output(struct mdfour *hash, const char *commands,
                          const char *compiler)
 {
-       char *command_string, *command, *p, *saveptr = NULL;
+       char *command_string = x_strdup(commands);
+       char *p = command_string;
+       char *command;
+       char *saveptr = NULL;
        bool ok = true;
-
-       command_string = x_strdup(commands);
-       p = command_string;
        while ((command = strtok_r(p, ";", &saveptr))) {
                if (!hash_command_output(hash, command, compiler)) {
                        ok = false;
index 77d6cc2..8d4321d 100644 (file)
@@ -1,27 +1,23 @@
-/*
- * Copyright (C) 2010, 2013 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
-/*
- * Supported file extensions and corresponding languages (as in parameter to
- * the -x option).
- */
+// Supported file extensions and corresponding languages (as in parameter to
+// the -x option).
 static const struct {
        const char *extension;
        const char *language;
@@ -41,12 +37,15 @@ static const struct {
        {".m",   "objective-c"},
        {".M",   "objective-c++"},
        {".mm",  "objective-c++"},
-       /* Preprocessed: */
+       {".sx",  "assembler-with-cpp"},
+       {".S",   "assembler-with-cpp"},
+       // Preprocessed:
        {".i",   "cpp-output"},
        {".ii",  "c++-cpp-output"},
        {".mi",  "objective-c-cpp-output"},
        {".mii", "objective-c++-cpp-output"},
-       /* Header file (for precompilation): */
+       {".s",   "assembler"},
+       // Header file (for precompilation):
        {".h",   "c-header"},
        {".H",   "c++-header"},
        {".h++", "c++-header"},
@@ -61,12 +60,36 @@ static const struct {
        {".HXX", "c++-header"},
        {".tcc", "c++-header"},
        {".TCC", "c++-header"},
+       {".cu",  "cuda"},
+       {".ic",  "cuda-output"},
+       // Fixed form Fortran without preprocessing:
+       {".f",   "f77"},
+       {".for", "f77"},
+       {".ftn", "f77"},
+       // Fixed form Fortran with traditional preprocessing:
+       {".F",   "f77-cpp-input"},
+       {".FOR", "f77-cpp-input"},
+       {".fpp", "f77-cpp-input"},
+       {".FPP", "f77-cpp-input"},
+       {".FTN", "f77-cpp-input"},
+       // Free form Fortran without preprocessing:
+#if 0 // Could generate modules, ignore for now!
+       {".f90", "f95"},
+       {".f95", "f95"},
+       {".f03", "f95"},
+       {".f08", "f95"},
+#endif
+       // Free form Fortran with traditional preprocessing:
+#if 0 // Could generate modules, ignore for now!
+       {".F90", "f95-cpp-input"},
+       {".F95", "f95-cpp-input"},
+       {".F03", "f95-cpp-input"},
+       {".F08", "f95-cpp-input"},
+#endif
        {NULL,  NULL}
 };
 
-/*
- * Supported languages and corresponding preprocessed languages.
- */
+// Supported languages and corresponding preprocessed languages.
 static const struct {
        const char *language;
        const char *p_language;
@@ -85,21 +108,25 @@ static const struct {
        {"objc++-cpp-output",        "objective-c++-cpp-output"},
        {"objective-c++-header",     "objective-c++-cpp-output"},
        {"objective-c++-cpp-output", "objective-c++-cpp-output"},
+       {"cuda",                     "cuda-output"},
+       {"assembler-with-cpp",       "assembler"},
+       {"assembler",                "assembler"},
+       {"f77-cpp-input",            "f77"},
+       {"f77",                      "f77"},
+#if 0 // Could generate module files, ignore for now!
+       {"f95-cpp-input",            "f95"},
+       {"f95",                      "f95"},
+#endif
        {NULL,  NULL}
 };
 
-/*
- * Guess the language of a file based on its extension. Returns NULL if the
- * extension is unknown.
- */
+// Guess the language of a file based on its extension. Returns NULL if the
+// extension is unknown.
 const char *
 language_for_file(const char *fname)
 {
-       int i;
-       const char *p;
-
-       p = get_extension(fname);
-       for (i = 0; extensions[i].extension; i++) {
+       const char *p = get_extension(fname);
+       for (int i = 0; extensions[i].extension; i++) {
                if (str_eq(p, extensions[i].extension)) {
                        return extensions[i].language;
                }
@@ -107,18 +134,14 @@ language_for_file(const char *fname)
        return NULL;
 }
 
-/*
- * Return the preprocessed language for a given language, or NULL if unknown.
- */
+// Return the preprocessed language for a given language, or NULL if unknown.
 const char *
 p_language_for_language(const char *language)
 {
-       int i;
-
        if (!language) {
                return NULL;
        }
-       for (i = 0; languages[i].language; ++i) {
+       for (int i = 0; languages[i].language; ++i) {
                if (str_eq(language, languages[i].language)) {
                        return languages[i].p_language;
                }
@@ -126,19 +149,15 @@ p_language_for_language(const char *language)
        return NULL;
 }
 
-/*
- * Return the default file extension (including dot) for a language, or NULL if
- * unknown.
- */
+// Return the default file extension (including dot) for a language, or NULL if
+// unknown.
 const char *
 extension_for_language(const char *language)
 {
-       int i;
-
        if (!language) {
                return NULL;
        }
-       for (i = 0; extensions[i].extension; i++) {
+       for (int i = 0; extensions[i].extension; i++) {
                if (str_eq(language, extensions[i].language)) {
                        return extensions[i].extension;
                }
index 6f674d1..ebfb8bb 100644 (file)
@@ -1,10 +1,12 @@
 #ifndef CCACHE_LANGUAGE_H
 #define CCACHE_LANGUAGE_H
 
+#include <stdbool.h>
+
 const char *language_for_file(const char *fname);
 const char *p_language_for_language(const char *language);
 const char *extension_for_language(const char *language);
 bool language_is_supported(const char *language);
 bool language_is_preprocessed(const char *language);
 
-#endif /* CCACHE_LANGUAGE_H */
+#endif // CCACHE_LANGUAGE_H
index b565e61..ce3dcf4 100644 (file)
@@ -1,77 +1,67 @@
-/*
- * Copyright (C) 2010-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
-/*
- * This function acquires a lockfile for the given path. Returns true if the
- * lock was acquired, otherwise false. If the lock has been considered stale
- * for the number of microseconds specified by staleness_limit, the function
- * will (if possible) break the lock and then try to acquire it again. The
- * staleness limit should be reasonably larger than the longest time the lock
- * can be expected to be held, and the updates of the locked path should
- * probably be made with an atomic rename(2) to avoid corruption in the rare
- * case that the lock is broken by another process.
- */
+// This function acquires a lockfile for the given path. Returns true if the
+// lock was acquired, otherwise false. If the lock has been considered stale
+// for the number of microseconds specified by staleness_limit, the function
+// will (if possible) break the lock and then try to acquire it again. The
+// staleness limit should be reasonably larger than the longest time the lock
+// can be expected to be held, and the updates of the locked path should
+// probably be made with an atomic rename(2) to avoid corruption in the rare
+// case that the lock is broken by another process.
 bool
 lockfile_acquire(const char *path, unsigned staleness_limit)
 {
-       int saved_errno = 0;
        char *lockfile = format("%s.lock", path);
-       char *my_content = NULL, *content = NULL, *initial_content = NULL;
+       char *my_content = NULL;
+       char *content = NULL;
+       char *initial_content = NULL;
        const char *hostname = get_hostname();
        bool acquired = false;
-#ifdef _WIN32
-       const size_t bufsize = 1024;
-       int fd, len;
-#else
-       int ret;
-#endif
-       unsigned to_sleep = 1000, slept = 0; /* Microseconds. */
+       unsigned to_sleep = 1000; // Microseconds.
+       unsigned slept = 0; // Microseconds.
 
        while (true) {
                free(my_content);
                my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(NULL));
 
 #ifdef _WIN32
-               fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0666);
+               int fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0666);
                if (fd == -1) {
-                       saved_errno = errno;
+                       int saved_errno = errno;
                        cc_log("lockfile_acquire: open WRONLY %s: %s", lockfile, strerror(errno));
                        if (saved_errno == ENOENT) {
-                               /* Directory doesn't exist? */
+                               // Directory doesn't exist?
                                if (create_parent_dirs(lockfile) == 0) {
-                                       /* OK. Retry. */
+                                       // OK. Retry.
                                        continue;
                                }
                        }
                        if (saved_errno != EEXIST) {
-                               /* Directory doesn't exist or isn't writable? */
+                               // Directory doesn't exist or isn't writable?
                                goto out;
                        }
-                       /* Someone else has the lock. */
+                       // Someone else has the lock.
                        fd = open(lockfile, O_RDONLY|O_BINARY);
                        if (fd == -1) {
                                if (errno == ENOENT) {
-                                       /*
-                                        * The file was removed after the open() call above, so retry
-                                        * acquiring it.
-                                        */
+                                       // The file was removed after the open() call above, so retry
+                                       // acquiring it.
                                        continue;
                                } else {
                                        cc_log("lockfile_acquire: open RDONLY %s: %s",
@@ -80,8 +70,10 @@ lockfile_acquire(const char *path, unsigned staleness_limit)
                                }
                        }
                        free(content);
+                       const size_t bufsize = 1024;
                        content = x_malloc(bufsize);
-                       if ((len = read(fd, content, bufsize - 1)) == -1) {
+                       int len = read(fd, content, bufsize - 1);
+                       if (len == -1) {
                                cc_log("lockfile_acquire: read %s: %s", lockfile, strerror(errno));
                                close(fd);
                                goto out;
@@ -89,7 +81,7 @@ lockfile_acquire(const char *path, unsigned staleness_limit)
                        close(fd);
                        content[len] = '\0';
                } else {
-                       /* We got the lock. */
+                       // We got the lock.
                        if (write(fd, my_content, strlen(my_content)) == -1) {
                                cc_log("lockfile_acquire: write %s: %s", lockfile, strerror(errno));
                                close(fd);
@@ -101,42 +93,37 @@ lockfile_acquire(const char *path, unsigned staleness_limit)
                        goto out;
                }
 #else
-               ret = symlink(my_content, lockfile);
-               if (ret == 0) {
-                       /* We got the lock. */
+               if (symlink(my_content, lockfile) == 0) {
+                       // We got the lock.
                        acquired = true;
                        goto out;
                }
-               saved_errno = errno;
+               int saved_errno = errno;
                cc_log("lockfile_acquire: symlink %s: %s", lockfile, strerror(saved_errno));
                if (saved_errno == ENOENT) {
-                       /* Directory doesn't exist? */
+                       // Directory doesn't exist?
                        if (create_parent_dirs(lockfile) == 0) {
-                               /* OK. Retry. */
+                               // OK. Retry.
                                continue;
                        }
                }
                if (saved_errno == EPERM) {
-                       /*
-                        * The file system does not support symbolic links. We have no choice but
-                        * to grant the lock anyway.
-                        */
+                       // The file system does not support symbolic links. We have no choice but
+                       // to grant the lock anyway.
                        acquired = true;
                        goto out;
                }
                if (saved_errno != EEXIST) {
-                       /* Directory doesn't exist or isn't writable? */
+                       // Directory doesn't exist or isn't writable?
                        goto out;
                }
                free(content);
                content = x_readlink(lockfile);
-               /* cppcheck-suppress nullPointer - false positive */
+               // cppcheck-suppress nullPointer - false positive
                if (!content) {
                        if (errno == ENOENT) {
-                               /*
-                                * The symlink was removed after the symlink() call above, so retry
-                                * acquiring it.
-                                */
+                               // The symlink was removed after the symlink() call above, so retry
+                               // acquiring it.
                                continue;
                        } else {
                                cc_log("lockfile_acquire: readlink %s: %s", lockfile, strerror(errno));
@@ -146,27 +133,26 @@ lockfile_acquire(const char *path, unsigned staleness_limit)
 #endif
 
                if (str_eq(content, my_content)) {
-                       /* Lost NFS reply? */
+                       // Lost NFS reply?
                        cc_log("lockfile_acquire: symlink %s failed but we got the lock anyway",
                               lockfile);
                        acquired = true;
                        goto out;
                }
-               /*
-                * A possible improvement here would be to check if the process holding the
-                * lock is still alive and break the lock early if it isn't.
-                */
+               // A possible improvement here would be to check if the process holding the
+               // lock is still alive and break the lock early if it isn't.
                cc_log("lockfile_acquire: lock info for %s: %s", lockfile, content);
                if (!initial_content) {
                        initial_content = x_strdup(content);
                }
                if (slept > staleness_limit) {
                        if (str_eq(content, initial_content)) {
-                               /* The lock seems to be stale -- break it. */
+                               // The lock seems to be stale -- break it.
                                cc_log("lockfile_acquire: breaking %s", lockfile);
+                               // Try to acquire path.lock.lock:
                                if (lockfile_acquire(lockfile, staleness_limit)) {
-                                       lockfile_release(path);
-                                       lockfile_release(lockfile);
+                                       lockfile_release(path); // Remove path.lock
+                                       lockfile_release(lockfile); // Remove path.lock.lock
                                        to_sleep = 1000;
                                        slept = 0;
                                        continue;
@@ -195,10 +181,8 @@ out:
        return acquired;
 }
 
-/*
- * Release the lockfile for the given path. Assumes that we are the legitimate
- * owner.
- */
+// Release the lockfile for the given path. Assumes that we are the legitimate
+// owner.
 void
 lockfile_release(const char *path)
 {
index cb32ec4..75243fe 100644 (file)
@@ -1,40 +1,43 @@
-/*
- * A Boyer-Moore-Horspool skip table used for searching for the strings
- * "__TIME__" and "__DATE__".
- *
- * macro_skip[c] = 8 for all c not in "__TIME__" and "__DATE__".
- *
- * The other characters map as follows:
- *
- *   _ -> 1
- *   A -> 4
- *   D -> 5
- *   E -> 2
- *   I -> 4
- *   M -> 3
- *   T -> 3
- *
- *
- * This was generated with the following Python script:
- *
- * m = {'_': 1,
- *      'A': 4,
- *      'D': 5,
- *      'E': 2,
- *      'I': 4,
- *      'M': 3,
- *      'T': 3}
- *
- * for i in range(0, 256):
- *     if chr(i) in m:
- *         num = m[chr(i)]
- *     else:
- *         num = 8
- *     print ("%d, " % num),
- *
- *     if i % 16 == 15:
- *         print ""
- */
+#ifndef CCACHE_MACROSKIP_H
+#define CCACHE_MACROSKIP_H
+
+#include <stdint.h>
+
+// A Boyer-Moore-Horspool skip table used for searching for the strings
+// "__TIME__" and "__DATE__".
+//
+// macro_skip[c] = 8 for all c not in "__TIME__" and "__DATE__".
+//
+// The other characters map as follows:
+//
+//   _ -> 1
+//   A -> 4
+//   D -> 5
+//   E -> 2
+//   I -> 4
+//   M -> 3
+//   T -> 3
+//
+//
+// This was generated with the following Python script:
+//
+// m = {'_': 1,
+//      'A': 4,
+//      'D': 5,
+//      'E': 2,
+//      'I': 4,
+//      'M': 3,
+//      'T': 3}
+//
+// for i in range(0, 256):
+//     if chr(i) in m:
+//         num = m[chr(i)]
+//     else:
+//         num = 8
+//     print ("%d, " % num),
+//
+//     if i % 16 == 15:
+//         print ""
 
 static const uint32_t macro_skip[] = {
        8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
@@ -54,3 +57,5 @@ static const uint32_t macro_skip[] = {
        8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
        8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 };
+
+#endif
diff --git a/main.c b/main.c
index 83c0abe..dda0b2e 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,22 +1,20 @@
-/*
- * ccache -- a fast C/C++ compiler cache
- *
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// ccache -- a fast C/C++ compiler cache
+//
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 int
 ccache_main(int argc, char *argv[]);
index 43ee01f..28ee35d 100644 (file)
@@ -1,20 +1,18 @@
-/*
- * Copyright (C) 2009-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 #include "hashtable_itr.h"
 
 #include <zlib.h>
 
-/*
- * Sketchy specification of the manifest disk format:
- *
- * <magic>         magic number                        (4 bytes)
- * <version>       file format version                 (1 byte unsigned int)
- * <hash_size>     size of the hash fields (in bytes)  (1 byte unsigned int)
- * <reserved>      reserved for future use             (2 bytes)
- * ----------------------------------------------------------------------------
- * <n>             number of include file paths        (4 bytes unsigned int)
- * <path_0>        path to include file                (NUL-terminated string,
- * ...                                                  at most 1024 bytes)
- * <path_n-1>
- * ----------------------------------------------------------------------------
- * <n>             number of include file hash entries (4 bytes unsigned int)
- * <index[0]>      index of include file path          (4 bytes unsigned int)
- * <hash[0]>       hash of include file                (<hash_size> bytes)
- * <size[0]>       size of include file                (4 bytes unsigned int)
- * <mtime[0]>      mtime of include file               (8 bytes signed int)
- * <ctime[0]>      ctime of include file               (8 bytes signed int)
- * ...
- * <index[n-1]>
- * <hash[n-1]>
- * <size[n-1]>
- * <mtime[n-1]>
- * <ctime[n-1]>
- * ----------------------------------------------------------------------------
- * <n>             number of object name entries       (4 bytes unsigned int)
- * <m[0]>          number of include file hash indexes (4 bytes unsigned int)
- * <index[0][0]>   include file hash index             (4 bytes unsigned int)
- * ...
- * <index[0][m[0]-1]>
- * <hash[0]>       hash part of object name            (<hash_size> bytes)
- * <size[0]>       size part of object name            (4 bytes unsigned int)
- * ...
- * <m[n-1]>        number of include file hash indexes
- * <index[n-1][0]> include file hash index
- * ...
- * <index[n-1][m[n-1]]>
- * <hash[n-1]>
- * <size[n-1]>
- */
+// Sketchy specification of the manifest disk format:
+//
+// <magic>         magic number                        (4 bytes)
+// <version>       file format version                 (1 byte unsigned int)
+// <hash_size>     size of the hash fields (in bytes)  (1 byte unsigned int)
+// <reserved>      reserved for future use             (2 bytes)
+// ----------------------------------------------------------------------------
+// <n>             number of include file paths        (4 bytes unsigned int)
+// <path_0>        path to include file                (NUL-terminated string,
+// ...                                                  at most 1024 bytes)
+// <path_n-1>
+// ----------------------------------------------------------------------------
+// <n>             number of include file hash entries (4 bytes unsigned int)
+// <index[0]>      index of include file path          (4 bytes unsigned int)
+// <hash[0]>       hash of include file                (<hash_size> bytes)
+// <size[0]>       size of include file                (4 bytes unsigned int)
+// <mtime[0]>      mtime of include file               (8 bytes signed int)
+// <ctime[0]>      ctime of include file               (8 bytes signed int)
+// ...
+// <index[n-1]>
+// <hash[n-1]>
+// <size[n-1]>
+// <mtime[n-1]>
+// <ctime[n-1]>
+// ----------------------------------------------------------------------------
+// <n>             number of object name entries       (4 bytes unsigned int)
+// <m[0]>          number of include file hash indexes (4 bytes unsigned int)
+// <index[0][0]>   include file hash index             (4 bytes unsigned int)
+// ...
+// <index[0][m[0]-1]>
+// <hash[0]>       hash part of object name            (<hash_size> bytes)
+// <size[0]>       size part of object name            (4 bytes unsigned int)
+// ...
+// <m[n-1]>        number of include file hash indexes
+// <index[n-1][0]> include file hash index
+// ...
+// <index[n-1][m[n-1]]>
+// <hash[n-1]>
+// <size[n-1]>
 
 static const uint32_t MAGIC = 0x63436d46U;
 static const uint32_t MAX_MANIFEST_ENTRIES = 100;
@@ -74,46 +70,46 @@ static const uint32_t MAX_MANIFEST_FILE_INFO_ENTRIES = 10000;
   do { enum { ccache_static_assert__ = 1/(e) }; } while (false)
 
 struct file_info {
-       /* Index to n_files. */
+       // Index to n_files.
        uint32_t index;
-       /* Hash of referenced file. */
+       // Hash of referenced file.
        uint8_t hash[16];
-       /* Size of referenced file. */
+       // Size of referenced file.
        uint32_t size;
-       /* mtime of referenced file. */
+       // mtime of referenced file.
        int64_t mtime;
-       /* ctime of referenced file. */
+       // ctime of referenced file.
        int64_t ctime;
 };
 
 struct object {
-       /* Number of entries in file_info_indexes. */
+       // Number of entries in file_info_indexes.
        uint32_t n_file_info_indexes;
-       /* Indexes to file_infos. */
+       // Indexes to file_infos.
        uint32_t *file_info_indexes;
-       /* Hash of the object itself. */
+       // Hash of the object itself.
        struct file_hash hash;
 };
 
 struct manifest {
-       /* Version of decoded file. */
+       // Version of decoded file.
        uint8_t version;
 
-       /* Reserved for future use. */
+       // Reserved for future use.
        uint16_t reserved;
 
-       /* Size of hash fields (in bytes). */
+       // Size of hash fields (in bytes).
        uint8_t hash_size;
 
-       /* Referenced include files. */
+       // Referenced include files.
        uint32_t n_files;
        char **files;
 
-       /* Information about referenced include files. */
+       // Information about referenced include files.
        uint32_t n_file_infos;
        struct file_info *file_infos;
 
-       /* Object names plus references to include file hashes. */
+       // Object names plus references to include file hashes.
        uint32_t n_objects;
        struct object *objects;
 };
@@ -127,7 +123,7 @@ struct file_stats {
 static unsigned int
 hash_from_file_info(void *key)
 {
-       ccache_static_assert(sizeof(struct file_info) == 40); /* No padding. */
+       ccache_static_assert(sizeof(struct file_info) == 40); // No padding.
        return murmurhashneutral2(key, sizeof(struct file_info), 0);
 }
 
@@ -146,13 +142,12 @@ file_infos_equal(void *key1, void *key2)
 static void
 free_manifest(struct manifest *mf)
 {
-       uint32_t i;
-       for (i = 0; i < mf->n_files; i++) {
+       for (uint32_t i = 0; i < mf->n_files; i++) {
                free(mf->files[i]);
        }
        free(mf->files);
        free(mf->file_infos);
-       for (i = 0; i < mf->n_objects; i++) {
+       for (uint32_t i = 0; i < mf->n_objects; i++) {
                free(mf->objects[i].file_info_indexes);
        }
        free(mf->objects);
@@ -161,8 +156,7 @@ free_manifest(struct manifest *mf)
 
 #define READ_BYTE(var) \
   do { \
-               int ch_; \
-               ch_ = gzgetc(f); \
+               int ch_ = gzgetc(f); \
                if (ch_ == EOF) { \
                        goto error; \
                } \
@@ -171,11 +165,9 @@ free_manifest(struct manifest *mf)
 
 #define READ_INT(size, var) \
   do { \
-               int ch_; \
-               size_t i_; \
                (var) = 0; \
-               for (i_ = 0; i_ < (size); i_++) { \
-                       ch_ = gzgetc(f); \
+               for (size_t i_ = 0; i_ < (size); i_++) { \
+                       int ch_ = gzgetc(f); \
                        if (ch_ == EOF) { \
                                goto error; \
                        } \
@@ -188,9 +180,8 @@ free_manifest(struct manifest *mf)
   do { \
                char buf_[1024]; \
                size_t i_; \
-               int ch_; \
                for (i_ = 0; i_ < sizeof(buf_); i_++) { \
-                       ch_ = gzgetc(f); \
+                       int ch_ = gzgetc(f); \
                        if (ch_ == EOF) { \
                                goto error; \
                        } \
@@ -207,10 +198,8 @@ free_manifest(struct manifest *mf)
 
 #define READ_BYTES(n, var) \
   do { \
-               size_t i_; \
-               int ch_; \
-               for (i_ = 0; i_ < (n); i_++) { \
-                       ch_ = gzgetc(f); \
+               for (size_t i_ = 0; i_ < (n); i_++) { \
+                       int ch_ = gzgetc(f); \
                        if (ch_ == EOF) { \
                                goto error; \
                        } \
@@ -221,9 +210,7 @@ free_manifest(struct manifest *mf)
 static struct manifest *
 create_empty_manifest(void)
 {
-       struct manifest *mf;
-
-       mf = x_malloc(sizeof(*mf));
+       struct manifest *mf = x_malloc(sizeof(*mf));
        mf->hash_size = 16;
        mf->n_files = 0;
        mf->files = NULL;
@@ -238,44 +225,39 @@ create_empty_manifest(void)
 static struct manifest *
 read_manifest(gzFile f)
 {
-       struct manifest *mf;
-       uint32_t i, j;
-       uint32_t magic;
-
-       mf = create_empty_manifest();
+       struct manifest *mf = create_empty_manifest();
 
+       uint32_t magic;
        READ_INT(4, magic);
        if (magic != MAGIC) {
                cc_log("Manifest file has bad magic number %u", magic);
-               free_manifest(mf);
-               return NULL;
+               goto error;
        }
+
        READ_BYTE(mf->version);
        if (mf->version != MANIFEST_VERSION) {
                cc_log("Manifest file has unknown version %u", mf->version);
-               free_manifest(mf);
-               return NULL;
+               goto error;
        }
 
        READ_BYTE(mf->hash_size);
        if (mf->hash_size != 16) {
-               /* Temporary measure until we support different hash algorithms. */
+               // Temporary measure until we support different hash algorithms.
                cc_log("Manifest file has unsupported hash size %u", mf->hash_size);
-               free_manifest(mf);
-               return NULL;
+               goto error;
        }
 
        READ_INT(2, mf->reserved);
 
        READ_INT(4, mf->n_files);
        mf->files = x_calloc(mf->n_files, sizeof(*mf->files));
-       for (i = 0; i < mf->n_files; i++) {
+       for (uint32_t i = 0; i < mf->n_files; i++) {
                READ_STR(mf->files[i]);
        }
 
        READ_INT(4, mf->n_file_infos);
        mf->file_infos = x_calloc(mf->n_file_infos, sizeof(*mf->file_infos));
-       for (i = 0; i < mf->n_file_infos; i++) {
+       for (uint32_t i = 0; i < mf->n_file_infos; i++) {
                READ_INT(4, mf->file_infos[i].index);
                READ_BYTES(mf->hash_size, mf->file_infos[i].hash);
                READ_INT(4, mf->file_infos[i].size);
@@ -285,12 +267,12 @@ read_manifest(gzFile f)
 
        READ_INT(4, mf->n_objects);
        mf->objects = x_calloc(mf->n_objects, sizeof(*mf->objects));
-       for (i = 0; i < mf->n_objects; i++) {
+       for (uint32_t i = 0; i < mf->n_objects; i++) {
                READ_INT(4, mf->objects[i].n_file_info_indexes);
                mf->objects[i].file_info_indexes =
                  x_calloc(mf->objects[i].n_file_info_indexes,
                           sizeof(*mf->objects[i].file_info_indexes));
-               for (j = 0; j < mf->objects[i].n_file_info_indexes; j++) {
+               for (uint32_t j = 0; j < mf->objects[i].n_file_info_indexes; j++) {
                        READ_INT(4, mf->objects[i].file_info_indexes[j]);
                }
                READ_BYTES(mf->hash_size, mf->objects[i].hash.hash);
@@ -337,20 +319,18 @@ error:
 static int
 write_manifest(gzFile f, const struct manifest *mf)
 {
-       uint32_t i, j;
-
        WRITE_INT(4, MAGIC);
        WRITE_INT(1, MANIFEST_VERSION);
        WRITE_INT(1, 16);
        WRITE_INT(2, 0);
 
        WRITE_INT(4, mf->n_files);
-       for (i = 0; i < mf->n_files; i++) {
+       for (uint32_t i = 0; i < mf->n_files; i++) {
                WRITE_STR(mf->files[i]);
        }
 
        WRITE_INT(4, mf->n_file_infos);
-       for (i = 0; i < mf->n_file_infos; i++) {
+       for (uint32_t i = 0; i < mf->n_file_infos; i++) {
                WRITE_INT(4, mf->file_infos[i].index);
                WRITE_BYTES(mf->hash_size, mf->file_infos[i].hash);
                WRITE_INT(4, mf->file_infos[i].size);
@@ -359,9 +339,9 @@ write_manifest(gzFile f, const struct manifest *mf)
        }
 
        WRITE_INT(4, mf->n_objects);
-       for (i = 0; i < mf->n_objects; i++) {
+       for (uint32_t i = 0; i < mf->n_objects; i++) {
                WRITE_INT(4, mf->objects[i].n_file_info_indexes);
-               for (j = 0; j < mf->objects[i].n_file_info_indexes; j++) {
+               for (uint32_t j = 0; j < mf->objects[i].n_file_info_indexes; j++) {
                        WRITE_INT(4, mf->objects[i].file_info_indexes[j]);
                }
                WRITE_BYTES(mf->hash_size, mf->objects[i].hash.hash);
@@ -379,12 +359,7 @@ static int
 verify_object(struct conf *conf, struct manifest *mf, struct object *obj,
               struct hashtable *stated_files, struct hashtable *hashed_files)
 {
-       uint32_t i;
-       struct file_hash *actual;
-       struct mdfour hash;
-       int result;
-
-       for (i = 0; i < obj->n_file_info_indexes; i++) {
+       for (uint32_t i = 0; i < obj->n_file_info_indexes; i++) {
                struct file_info *fi = &mf->file_infos[obj->file_info_indexes[i]];
                char *path = mf->files[fi->index];
                struct file_stats *st = hashtable_search(stated_files, path);
@@ -401,11 +376,9 @@ verify_object(struct conf *conf, struct manifest *mf, struct object *obj,
                }
 
                if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) {
-                       /*
-                        * st->ctime is sometimes 0, so we can't check that both st->ctime and
-                        * st->mtime are greater than time_of_compilation. But it's sufficient to
-                        * check that either is.
-                        */
+                       // st->ctime is sometimes 0, so we can't check that both st->ctime and
+                       // st->mtime are greater than time_of_compilation. But it's sufficient to
+                       // check that either is.
                        if (fi->size == st->size
                            && fi->mtime == st->mtime
                            && fi->ctime == st->ctime
@@ -417,20 +390,19 @@ verify_object(struct conf *conf, struct manifest *mf, struct object *obj,
                        }
                }
 
-               actual = hashtable_search(hashed_files, path);
+               struct file_hash *actual = hashtable_search(hashed_files, path);
                if (!actual) {
-                       actual = x_malloc(sizeof(*actual));
+                       struct mdfour hash;
                        hash_start(&hash);
-                       result = hash_source_code_file(conf, &hash, path);
+                       int result = hash_source_code_file(conf, &hash, path);
                        if (result & HASH_SOURCE_CODE_ERROR) {
                                cc_log("Failed hashing %s", path);
-                               free(actual);
                                return 0;
                        }
                        if (result & HASH_SOURCE_CODE_FOUND_TIME) {
-                               free(actual);
                                return 0;
                        }
+                       actual = x_malloc(sizeof(*actual));
                        hash_result_as_bytes(&hash, actual->hash);
                        actual->size = hash.totalN;
                        hashtable_insert(hashed_files, x_strdup(path), actual);
@@ -447,13 +419,10 @@ verify_object(struct conf *conf, struct manifest *mf, struct object *obj,
 static struct hashtable *
 create_string_index_map(char **strings, uint32_t len)
 {
-       uint32_t i;
-       struct hashtable *h;
-       uint32_t *index;
-
-       h = create_hashtable(1000, hash_from_string, strings_equal);
-       for (i = 0; i < len; i++) {
-               index = x_malloc(sizeof(*index));
+       struct hashtable *h =
+         create_hashtable(1000, hash_from_string, strings_equal);
+       for (uint32_t i = 0; i < len; i++) {
+               uint32_t *index = x_malloc(sizeof(*index));
                *index = i;
                hashtable_insert(h, x_strdup(strings[i]), index);
        }
@@ -463,16 +432,12 @@ create_string_index_map(char **strings, uint32_t len)
 static struct hashtable *
 create_file_info_index_map(struct file_info *infos, uint32_t len)
 {
-       uint32_t i;
-       struct hashtable *h;
-       struct file_info *fi;
-       uint32_t *index;
-
-       h = create_hashtable(1000, hash_from_file_info, file_infos_equal);
-       for (i = 0; i < len; i++) {
-               fi = x_malloc(sizeof(*fi));
+       struct hashtable *h =
+         create_hashtable(1000, hash_from_file_info, file_infos_equal);
+       for (uint32_t i = 0; i < len; i++) {
+               struct file_info *fi = x_malloc(sizeof(*fi));
                *fi = infos[i];
-               index = x_malloc(sizeof(*index));
+               uint32_t *index = x_malloc(sizeof(*index));
                *index = i;
                hashtable_insert(h, fi, index);
        }
@@ -483,19 +448,15 @@ static uint32_t
 get_include_file_index(struct manifest *mf, char *path,
                        struct hashtable *mf_files)
 {
-       uint32_t *index;
-       uint32_t n;
-
-       index = hashtable_search(mf_files, path);
+       uint32_t *index = hashtable_search(mf_files, path);
        if (index) {
                return *index;
        }
 
-       n = mf->n_files;
+       uint32_t n = mf->n_files;
        mf->files = x_realloc(mf->files, (n + 1) * sizeof(*mf->files));
        mf->n_files++;
        mf->files[n] = x_strdup(path);
-
        return n;
 }
 
@@ -507,23 +468,18 @@ get_file_hash_index(struct manifest *mf,
                     struct hashtable *mf_file_infos)
 {
        struct file_info fi;
-       uint32_t *fi_index;
-       uint32_t n;
-       struct stat file_stat;
-
        fi.index = get_include_file_index(mf, path, mf_files);
        memcpy(fi.hash, file_hash->hash, sizeof(fi.hash));
        fi.size = file_hash->size;
 
-       /*
-        * file_stat.st_{m,c}time has a resolution of 1 second, so we can cache the
-        * file's mtime and ctime only if they're at least one second older than
-        * time_of_compilation.
-        *
-        * st->ctime may be 0, so we have to check time_of_compilation against
-        * MAX(mtime, ctime).
-        */
+       // file_stat.st_{m,c}time has a resolution of 1 second, so we can cache the
+       // file's mtime and ctime only if they're at least one second older than
+       // time_of_compilation.
+       //
+       // st->ctime may be 0, so we have to check time_of_compilation against
+       // MAX(mtime, ctime).
 
+       struct stat file_stat;
        if (stat(path, &file_stat) != -1
            && time_of_compilation > MAX(file_stat.st_mtime, file_stat.st_ctime)) {
                fi.mtime = file_stat.st_mtime;
@@ -533,16 +489,15 @@ get_file_hash_index(struct manifest *mf,
                fi.ctime = -1;
        }
 
-       fi_index = hashtable_search(mf_file_infos, &fi);
+       uint32_t *fi_index = hashtable_search(mf_file_infos, &fi);
        if (fi_index) {
                return *fi_index;
        }
 
-       n = mf->n_file_infos;
+       uint32_t n = mf->n_file_infos;
        mf->file_infos = x_realloc(mf->file_infos, (n + 1) * sizeof(*mf->file_infos));
        mf->n_file_infos++;
        mf->file_infos[n] = fi;
-
        return n;
 }
 
@@ -550,19 +505,18 @@ static void
 add_file_info_indexes(uint32_t *indexes, uint32_t size,
                       struct manifest *mf, struct hashtable *included_files)
 {
-       struct hashtable_itr *iter;
-       uint32_t i;
-       struct hashtable *mf_files; /* path --> index */
-       struct hashtable *mf_file_infos; /* struct file_info --> index */
-
        if (size == 0) {
                return;
        }
 
-       mf_files = create_string_index_map(mf->files, mf->n_files);
-       mf_file_infos = create_file_info_index_map(mf->file_infos, mf->n_file_infos);
-       iter = hashtable_iterator(included_files);
-       i = 0;
+       // path --> index
+       struct hashtable *mf_files =
+         create_string_index_map(mf->files, mf->n_files);
+       // struct file_info --> index
+       struct hashtable *mf_file_infos =
+         create_file_info_index_map(mf->file_infos, mf->n_file_infos);
+       struct hashtable_itr *iter = hashtable_iterator(included_files);
+       uint32_t i = 0;
        do {
                char *path = hashtable_iterator_key(iter);
                struct file_hash *file_hash = hashtable_iterator_value(iter);
@@ -581,40 +535,33 @@ add_object_entry(struct manifest *mf,
                  struct file_hash *object_hash,
                  struct hashtable *included_files)
 {
-       struct object *obj;
-       uint32_t n;
-
-       n = mf->n_objects;
-       mf->objects = x_realloc(mf->objects, (n + 1) * sizeof(*mf->objects));
+       uint32_t n_objs = mf->n_objects;
+       mf->objects = x_realloc(mf->objects, (n_objs + 1) * sizeof(*mf->objects));
        mf->n_objects++;
-       obj = &mf->objects[n];
+       struct object *obj = &mf->objects[n_objs];
 
-       n = hashtable_count(included_files);
-       obj->n_file_info_indexes = n;
-       obj->file_info_indexes = x_malloc(n * sizeof(*obj->file_info_indexes));
-       add_file_info_indexes(obj->file_info_indexes, n, mf, included_files);
+       uint32_t n_fii = hashtable_count(included_files);
+       obj->n_file_info_indexes = n_fii;
+       obj->file_info_indexes = x_malloc(n_fii * sizeof(*obj->file_info_indexes));
+       add_file_info_indexes(obj->file_info_indexes, n_fii, mf, included_files);
        memcpy(obj->hash.hash, object_hash->hash, mf->hash_size);
        obj->hash.size = object_hash->size;
 }
 
-/*
- * Try to get the object hash from a manifest file. Caller frees. Returns NULL
- * on failure.
- */
+// Try to get the object hash from a manifest file. Caller frees. Returns NULL
+// on failure.
 struct file_hash *
 manifest_get(struct conf *conf, const char *manifest_path)
 {
-       int fd;
        gzFile f = NULL;
        struct manifest *mf = NULL;
-       struct hashtable *hashed_files = NULL; /* path --> struct file_hash */
-       struct hashtable *stated_files = NULL; /* path --> struct file_stats */
-       uint32_t i;
+       struct hashtable *hashed_files = NULL; // path --> struct file_hash
+       struct hashtable *stated_files = NULL; // path --> struct file_stats
        struct file_hash *fh = NULL;
 
-       fd = open(manifest_path, O_RDONLY | O_BINARY);
+       int fd = open(manifest_path, O_RDONLY | O_BINARY);
        if (fd == -1) {
-               /* Cache miss. */
+               // Cache miss.
                cc_log("No such manifest file");
                goto out;
        }
@@ -633,8 +580,8 @@ manifest_get(struct conf *conf, const char *manifest_path)
        hashed_files = create_hashtable(1000, hash_from_string, strings_equal);
        stated_files = create_hashtable(1000, hash_from_string, strings_equal);
 
-       /* Check newest object first since it's a bit more likely to match. */
-       for (i = mf->n_objects; i > 0; i--) {
+       // Check newest object first since it's a bit more likely to match.
+       for (uint32_t i = mf->n_objects; i > 0; i--) {
                if (verify_object(conf, mf, &mf->objects[i - 1],
                                  stated_files, hashed_files)) {
                        fh = x_malloc(sizeof(*fh));
@@ -659,30 +606,24 @@ out:
        return fh;
 }
 
-/*
- * Put the object name into a manifest file given a set of included files.
- * Returns true on success, otherwise false.
- */
+// Put the object name into a manifest file given a set of included files.
+// Returns true on success, otherwise false.
 bool
 manifest_put(const char *manifest_path, struct file_hash *object_hash,
              struct hashtable *included_files)
 {
        int ret = 0;
-       int fd1;
-       int fd2;
        gzFile f2 = NULL;
        struct manifest *mf = NULL;
        char *tmp_file = NULL;
 
-       /*
-        * We don't bother to acquire a lock when writing the manifest to disk. A
-        * race between two processes will only result in one lost entry, which is
-        * not a big deal, and it's also very unlikely.
-        */
+       // We don't bother to acquire a lock when writing the manifest to disk. A
+       // race between two processes will only result in one lost entry, which is
+       // not a big deal, and it's also very unlikely.
 
-       fd1 = open(manifest_path, O_RDONLY | O_BINARY);
+       int fd1 = open(manifest_path, O_RDONLY | O_BINARY);
        if (fd1 == -1) {
-               /* New file. */
+               // New file.
                mf = create_empty_manifest();
        } else {
                gzFile f1 = gzdopen(fd1, "rb");
@@ -701,27 +642,24 @@ manifest_put(const char *manifest_path, struct file_hash *object_hash,
        }
 
        if (mf->n_objects > MAX_MANIFEST_ENTRIES) {
-               /*
-                * Normally, there shouldn't be many object entries in the manifest since
-                * new entries are added only if an include file has changed but not the
-                * source file, and you typically change source files more often than
-                * header files. However, it's certainly possible to imagine cases where
-                * the manifest will grow large (for instance, a generated header file that
-                * changes for every build), and this must be taken care of since
-                * processing an ever growing manifest eventually will take too much time.
-                * A good way of solving this would be to maintain the object entries in
-                * LRU order and discarding the old ones. An easy way is to throw away all
-                * entries when there are too many. Let's do that for now.
-                */
+               // Normally, there shouldn't be many object entries in the manifest since
+               // new entries are added only if an include file has changed but not the
+               // source file, and you typically change source files more often than
+               // header files. However, it's certainly possible to imagine cases where
+               // the manifest will grow large (for instance, a generated header file that
+               // changes for every build), and this must be taken care of since
+               // processing an ever growing manifest eventually will take too much time.
+               // A good way of solving this would be to maintain the object entries in
+               // LRU order and discarding the old ones. An easy way is to throw away all
+               // entries when there are too many. Let's do that for now.
                cc_log("More than %u entries in manifest file; discarding",
                       MAX_MANIFEST_ENTRIES);
                free_manifest(mf);
                mf = create_empty_manifest();
        } else if (mf->n_file_infos > MAX_MANIFEST_FILE_INFO_ENTRIES) {
-               /* Rarely, file_info entries can grow large in pathological cases where
-                * many included files change, but the main file does not. This also puts
-                * an upper bound on the number of file_info entries.
-                */
+               // Rarely, file_info entries can grow large in pathological cases where
+               // many included files change, but the main file does not. This also puts
+               // an upper bound on the number of file_info entries.
                cc_log("More than %u file_info entries in manifest file; discarding",
                       MAX_MANIFEST_FILE_INFO_ENTRIES);
                free_manifest(mf);
@@ -729,7 +667,7 @@ manifest_put(const char *manifest_path, struct file_hash *object_hash,
        }
 
        tmp_file = format("%s.tmp", manifest_path);
-       fd2 = create_tmp_fd(&tmp_file);
+       int fd2 = create_tmp_fd(&tmp_file);
        f2 = gzdopen(fd2, "wb");
        if (!f2) {
                cc_log("Failed to gzdopen %s", tmp_file);
@@ -768,12 +706,10 @@ bool
 manifest_dump(const char *manifest_path, FILE *stream)
 {
        struct manifest *mf = NULL;
-       int fd;
        gzFile f = NULL;
        bool ret = false;
-       unsigned i, j;
 
-       fd = open(manifest_path, O_RDONLY | O_BINARY);
+       int fd = open(manifest_path, O_RDONLY | O_BINARY);
        if (fd == -1) {
                fprintf(stderr, "No such manifest file: %s\n", manifest_path);
                goto out;
@@ -799,11 +735,11 @@ manifest_dump(const char *manifest_path, FILE *stream)
        fprintf(stream, "Hash size: %u\n", (unsigned)mf->hash_size);
        fprintf(stream, "Reserved field: %u\n", (unsigned)mf->reserved);
        fprintf(stream, "File paths (%u):\n", (unsigned)mf->n_files);
-       for (i = 0; i < mf->n_files; ++i) {
+       for (unsigned i = 0; i < mf->n_files; ++i) {
                fprintf(stream, "  %u: %s\n", i, mf->files[i]);
        }
        fprintf(stream, "File infos (%u):\n", (unsigned)mf->n_file_infos);
-       for (i = 0; i < mf->n_file_infos; ++i) {
+       for (unsigned i = 0; i < mf->n_file_infos; ++i) {
                char *hash;
                fprintf(stream, "  %u:\n", i);
                fprintf(stream, "    Path index: %u\n", mf->file_infos[i].index);
@@ -815,11 +751,11 @@ manifest_dump(const char *manifest_path, FILE *stream)
                fprintf(stream, "    Ctime: %lld\n", (long long)mf->file_infos[i].ctime);
        }
        fprintf(stream, "Results (%u):\n", (unsigned)mf->n_objects);
-       for (i = 0; i < mf->n_objects; ++i) {
+       for (unsigned i = 0; i < mf->n_objects; ++i) {
                char *hash;
                fprintf(stream, "  %u:\n", i);
                fprintf(stream, "    File info indexes:");
-               for (j = 0; j < mf->objects[i].n_file_info_indexes; ++j) {
+               for (unsigned j = 0; j < mf->objects[i].n_file_info_indexes; ++j) {
                        fprintf(stream, " %u", mf->objects[i].file_info_indexes[j]);
                }
                fprintf(stream, "\n");
index 73736ba..36b55fe 100644 (file)
--- a/mdfour.c
+++ b/mdfour.c
@@ -1,24 +1,23 @@
-/*
- * Copyright (C) 1997-1998 Andrew Tridgell
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 1997-1998 Andrew Tridgell
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
-/* NOTE: This code makes no attempt to be fast! */
+// NOTE: This code makes no attempt to be fast!
 
 static struct mdfour *m;
 
@@ -36,15 +35,21 @@ static struct mdfour *m;
 #define ROUND3(a, b, c, d, k, s) \
   a = lshift((a + H(b, c, d) + M[k] + 0x6ED9EBA1)&MASK32, s)
 
-/* this applies md4 to 64 byte chunks */
+// This applies md4 to 64 byte chunks.
 static void
 mdfour64(uint32_t *M)
 {
        uint32_t AA, BB, CC, DD;
        uint32_t A, B, C, D;
 
-       A = m->A; B = m->B; C = m->C; D = m->D;
-       AA = A; BB = B; CC = C; DD = D;
+       A = m->A;
+       B = m->B;
+       C = m->C;
+       D = m->D;
+       AA = A;
+       BB = B;
+       CC = C;
+       DD = D;
 
        ROUND1(A, B, C, D,  0,  3);  ROUND1(D, A, B, C,  1,  7);
        ROUND1(C, D, A, B,  2, 11);  ROUND1(B, C, D, A,  3, 19);
@@ -74,24 +79,30 @@ mdfour64(uint32_t *M)
        ROUND3(A, B, C, D,  3,  3);  ROUND3(D, A, B, C, 11,  9);
        ROUND3(C, D, A, B,  7, 11);  ROUND3(B, C, D, A, 15, 15);
 
-       A += AA; B += BB;
-       C += CC; D += DD;
+       A += AA;
+       B += BB;
+       C += CC;
+       D += DD;
 
-       A &= MASK32; B &= MASK32;
-       C &= MASK32; D &= MASK32;
+       A &= MASK32;
+       B &= MASK32;
+       C &= MASK32;
+       D &= MASK32;
 
-       m->A = A; m->B = B; m->C = C; m->D = D;
+       m->A = A;
+       m->B = B;
+       m->C = C;
+       m->D = D;
 }
 
 static void
 copy64(uint32_t *M, const unsigned char *in)
 {
 #ifdef WORDS_BIGENDIAN
-       int i;
-
-       for (i = 0; i < 16; i++)
+       for (int i = 0; i < 16; i++) {
                M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
                       (in[i*4+1]<<8) | (in[i*4+0]<<0);
+       }
 #else
        memcpy(M, in, 16*4);
 #endif
@@ -125,14 +136,10 @@ mdfour_begin(struct mdfour *md)
 static
 void mdfour_tail(const unsigned char *in, size_t n)
 {
+       m->totalN += n;
+       uint32_t b = m->totalN * 8;
        unsigned char buf[128] = { 0 };
        uint32_t M[16];
-       uint32_t b;
-
-       m->totalN += n;
-
-       b = m->totalN * 8;
-
        if (n) {
                memcpy(buf, in, n);
        }
@@ -154,8 +161,6 @@ void mdfour_tail(const unsigned char *in, size_t n)
 void
 mdfour_update(struct mdfour *md, const unsigned char *in, size_t n)
 {
-       uint32_t M[16];
-
 #ifdef CCACHE_DEBUG_HASH
        if (getenv("CCACHE_DEBUG_HASH")) {
                FILE *f = fopen("ccache-debug-hash.bin", "a");
@@ -174,6 +179,7 @@ mdfour_update(struct mdfour *md, const unsigned char *in, size_t n)
                return;
        }
 
+       uint32_t M[16];
        if (md->tail_len) {
                size_t len = 64 - md->tail_len;
                if (len > n) {
index cd89133..b568af6 100644 (file)
@@ -1,7 +1,5 @@
-/*
- * MurmurHashNeutral2, by Austin Appleby. Released to the public domain. See
- * <http://murmurhash.googlepages.com>.
- */
+// MurmurHashNeutral2, by Austin Appleby. Released to the public domain. See
+// <http://murmurhash.googlepages.com>.
 
 #include "murmurhashneutral2.h"
 
@@ -10,15 +8,11 @@ murmurhashneutral2(const void *key, int len, unsigned int seed)
 {
        const unsigned int m = 0x5bd1e995;
        const int r = 24;
-
        unsigned int h = seed ^ len;
-
        const unsigned char *data = (const unsigned char *)key;
 
        while (len >= 4) {
-               unsigned int k;
-
-               k = data[0];
+               unsigned int k = data[0];
                k |= data[1] << 8;
                k |= data[2] << 16;
                k |= data[3] << 24;
index e1b86f2..b935ee9 100644 (file)
@@ -539,7 +539,9 @@ static UINTMAX_T cast(LDOUBLE);
 static UINTMAX_T myround(LDOUBLE);
 static LDOUBLE mypow10(int);
 
+#ifndef __MINGW32__
 extern int errno;
+#endif
 
 int
 rpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
diff --git a/stats.c b/stats.c
index 5f21765..6eff3e1 100644 (file)
--- a/stats.c
+++ b/stats.c
@@ -1,26 +1,22 @@
-/*
- * Copyright (C) 2002-2004 Andrew Tridgell
- * Copyright (C) 2009-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * Routines to handle the stats files. The stats file is stored one per cache
- * subdirectory to make this more scalable.
- */
+// Copyright (C) 2002-2004 Andrew Tridgell
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// Routines to handle the stats files. The stats file is stored one per cache
+// subdirectory to make this more scalable.
 
 #include "ccache.h"
 #include "hashutil.h"
@@ -41,13 +37,13 @@ extern char *secondary_config_path;
 
 static struct counters *counter_updates;
 
-#define FLAG_NOZERO 1 /* don't zero with the -z option */
-#define FLAG_ALWAYS 2 /* always show, even if zero */
-#define FLAG_NEVER 4 /* never show */
+#define FLAG_NOZERO 1 // don't zero with the -z option
+#define FLAG_ALWAYS 2 // always show, even if zero
+#define FLAG_NEVER 4 // never show
 
 static void display_size_times_1024(uint64_t size);
 
-/* statistics fields in display order */
+// Statistics fields in display order.
 static struct {
        enum stats stat;
        char *message;
@@ -78,6 +74,7 @@ static struct {
        { STATS_DEVICE,       "output to a non-regular file   ", NULL, 0 },
        { STATS_NOINPUT,      "no input file                  ", NULL, 0 },
        { STATS_BADEXTRAFILE, "error hashing extra file       ", NULL, 0 },
+       { STATS_NUMCLEANUPS,  "cleanups performed             ", NULL, FLAG_ALWAYS },
        { STATS_NUMFILES,     "files in cache                 ", NULL,
                FLAG_NOZERO|FLAG_ALWAYS },
        { STATS_TOTALSIZE,    "cache size                     ",
@@ -103,16 +100,14 @@ display_size_times_1024(uint64_t size)
        display_size(size * 1024);
 }
 
-/* parse a stats file from a buffer - adding to the counters */
+// Parse a stats file from a buffer, adding to the counters.
 static void
 parse_stats(struct counters *counters, const char *buf)
 {
        size_t i = 0;
-       const char *p;
-       char *p2;
-
-       p = buf;
+       const char *p = buf;
        while (true) {
+               char *p2;
                long val = strtol(p, &p2, 10);
                if (p2 == p) {
                        break;
@@ -126,17 +121,13 @@ parse_stats(struct counters *counters, const char *buf)
        }
 }
 
-/* write out a stats file */
+// Write out a stats file.
 void
 stats_write(const char *path, struct counters *counters)
 {
-       size_t i;
-       char *tmp_file;
-       FILE *f;
-
-       tmp_file = format("%s.tmp", path);
-       f = create_tmp_file(&tmp_file, "wb");
-       for (i = 0; i < counters->size; i++) {
+       char *tmp_file = format("%s.tmp", path);
+       FILE *f = create_tmp_file(&tmp_file, "wb");
+       for (size_t i = 0; i < counters->size; i++) {
                if (fprintf(f, "%u\n", counters->data[i]) < 0) {
                        fatal("Failed to write to %s", tmp_file);
                }
@@ -154,10 +145,8 @@ init_counter_updates(void)
        }
 }
 
-/*
- * Record that a number of bytes and files have been added to the cache. Size
- * is in bytes.
- */
+// Record that a number of bytes and files have been added to the cache. Size
+// is in bytes.
 void
 stats_update_size(uint64_t size, unsigned files)
 {
@@ -166,7 +155,7 @@ stats_update_size(uint64_t size, unsigned files)
        counter_updates->data[STATS_TOTALSIZE] += size / 1024;
 }
 
-/* Read in the stats from one directory and add to the counters. */
+// Read in the stats from one directory and add to the counters.
 void
 stats_read(const char *sfile, struct counters *counters)
 {
@@ -177,17 +166,10 @@ stats_read(const char *sfile, struct counters *counters)
        free(data);
 }
 
-/*
- * Write counter updates in counter_updates to disk.
- */
+// Write counter updates in counter_updates to disk.
 void
 stats_flush(void)
 {
-       struct counters *counters;
-       bool need_cleanup = false;
-       bool should_flush = false;
-       int i;
-
        assert(conf);
 
        if (!conf->stats) {
@@ -198,7 +180,8 @@ stats_flush(void)
                return;
        }
 
-       for (i = 0; i < STATS_END; ++i) {
+       bool should_flush = false;
+       for (int i = 0; i < STATS_END; ++i) {
                if (counter_updates->data[i] > 0) {
                        should_flush = true;
                        break;
@@ -211,10 +194,8 @@ stats_flush(void)
        if (!stats_file) {
                char *stats_dir;
 
-               /*
-                * A NULL stats_file means that we didn't get past calculate_object_hash(),
-                * so we just choose one of stats files in the 16 subdirectories.
-                */
+               // A NULL stats_file means that we didn't get past calculate_object_hash(),
+               // so we just choose one of stats files in the 16 subdirectories.
                stats_dir = format("%s/%x", conf->cache_dir, hash_from_int(getpid()) % 16);
                stats_file = format("%s/stats", stats_dir);
                free(stats_dir);
@@ -223,16 +204,17 @@ stats_flush(void)
        if (!lockfile_acquire(stats_file, lock_staleness_limit)) {
                return;
        }
-       counters = counters_init(STATS_END);
+
+       struct counters *counters = counters_init(STATS_END);
        stats_read(stats_file, counters);
-       for (i = 0; i < STATS_END; ++i) {
+       for (int i = 0; i < STATS_END; ++i) {
                counters->data[i] += counter_updates->data[i];
        }
        stats_write(stats_file, counters);
        lockfile_release(stats_file);
 
        if (!str_eq(conf->log_file, "")) {
-               for (i = 0; i < STATS_END; ++i) {
+               for (int i = 0; i < STATS_END; ++i) {
                        if (counter_updates->data[stats_info[i].stat] != 0
                            && !(stats_info[i].flags & FLAG_NOZERO)) {
                                cc_log("Result: %s", stats_info[i].message);
@@ -240,6 +222,7 @@ stats_flush(void)
                }
        }
 
+       bool need_cleanup = false;
        if (conf->max_files != 0
            && counters->data[STATS_NUMFILES] > conf->max_files / 16) {
                need_cleanup = true;
@@ -258,7 +241,7 @@ stats_flush(void)
        counters_free(counters);
 }
 
-/* update a normal stat */
+// Update a normal stat.
 void
 stats_update(enum stats stat)
 {
@@ -267,7 +250,7 @@ stats_update(enum stats stat)
        counter_updates->data[stat]++;
 }
 
-/* Get the pending update of a counter value. */
+// Get the pending update of a counter value.
 unsigned
 stats_get_pending(enum stats stat)
 {
@@ -275,17 +258,16 @@ stats_get_pending(enum stats stat)
        return counter_updates->data[stat];
 }
 
-/* sum and display the total stats for all cache dirs */
+// Sum and display the total stats for all cache dirs.
 void
 stats_summary(struct conf *conf)
 {
-       int dir, i;
        struct counters *counters = counters_init(STATS_END);
 
        assert(conf);
 
-       /* add up the stats in each directory */
-       for (dir = -1; dir <= 0xF; dir++) {
+       // Add up the stats in each directory.
+       for (int dir = -1; dir <= 0xF; dir++) {
                char *fname;
 
                if (dir == -1) {
@@ -304,8 +286,8 @@ stats_summary(struct conf *conf)
        printf("secondary config      (readonly)    %s\n",
               secondary_config_path ? secondary_config_path : "");
 
-       /* and display them */
-       for (i = 0; stats_info[i].message; i++) {
+       // ...and display them.
+       for (int i = 0; stats_info[i].message; i++) {
                enum stats stat = stats_info[i].stat;
 
                if (stats_info[i].flags & FLAG_NEVER) {
@@ -322,6 +304,16 @@ stats_summary(struct conf *conf)
                } else {
                        printf("%8u\n", counters->data[stat]);
                }
+
+               if (stat == STATS_TOCACHE) {
+                       unsigned direct = counters->data[STATS_CACHEHIT_DIR];
+                       unsigned preprocessed = counters->data[STATS_CACHEHIT_CPP];
+                       unsigned hit = direct + preprocessed;
+                       unsigned miss = counters->data[STATS_TOCACHE];
+                       unsigned total = hit + miss;
+                       double percent = total > 0 ? (100.0f * hit) / total : 0.0f;
+                       printf("cache hit rate                    %6.2f %%\n", percent);
+               }
        }
 
        if (conf->max_files != 0) {
@@ -336,32 +328,28 @@ stats_summary(struct conf *conf)
        counters_free(counters);
 }
 
-/* zero all the stats structures */
+// Zero all the stats structures.
 void
 stats_zero(void)
 {
-       int dir;
-       unsigned i;
-       char *fname;
-
        assert(conf);
 
-       fname = format("%s/stats", conf->cache_dir);
+       char *fname = format("%s/stats", conf->cache_dir);
        x_unlink(fname);
        free(fname);
 
-       for (dir = 0; dir <= 0xF; dir++) {
+       for (int dir = 0; dir <= 0xF; dir++) {
                struct counters *counters = counters_init(STATS_END);
                struct stat st;
                fname = format("%s/%1x/stats", conf->cache_dir, dir);
                if (stat(fname, &st) != 0) {
-                       /* No point in trying to reset the stats file if it doesn't exist. */
+                       // No point in trying to reset the stats file if it doesn't exist.
                        free(fname);
                        continue;
                }
                if (lockfile_acquire(fname, lock_staleness_limit)) {
                        stats_read(fname, counters);
-                       for (i = 0; stats_info[i].message; i++) {
+                       for (unsigned i = 0; stats_info[i].message; i++) {
                                if (!(stats_info[i].flags & FLAG_NOZERO)) {
                                        counters->data[stats_info[i].stat] = 0;
                                }
@@ -374,7 +362,7 @@ stats_zero(void)
        }
 }
 
-/* Get the per directory limits */
+// Get the per-directory limits.
 void
 stats_get_obsolete_limits(const char *dir, unsigned *maxfiles,
                           uint64_t *maxsize)
@@ -388,15 +376,12 @@ stats_get_obsolete_limits(const char *dir, unsigned *maxfiles,
        counters_free(counters);
 }
 
-/* set the per directory sizes */
+// Set the per-directory sizes.
 void
 stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size)
 {
        struct counters *counters = counters_init(STATS_END);
-       char *statsfile;
-
-       statsfile = format("%s/stats", dir);
-
+       char *statsfile = format("%s/stats", dir);
        if (lockfile_acquire(statsfile, lock_staleness_limit)) {
                stats_read(statsfile, counters);
                counters->data[STATS_NUMFILES] = num_files;
@@ -407,3 +392,19 @@ stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size)
        free(statsfile);
        counters_free(counters);
 }
+
+// Count directory cleanup run.
+void
+stats_add_cleanup(const char *dir, unsigned count)
+{
+       struct counters *counters = counters_init(STATS_END);
+       char *statsfile = format("%s/stats", dir);
+       if (lockfile_acquire(statsfile, lock_staleness_limit)) {
+               stats_read(statsfile, counters);
+               counters->data[STATS_NUMCLEANUPS] += count;
+               stats_write(statsfile, counters);
+               lockfile_release(statsfile);
+       }
+       free(statsfile);
+       counters_free(counters);
+}
index ca17b49..0de03da 100644 (file)
--- a/system.h
+++ b/system.h
@@ -1,20 +1,18 @@
-/*
- * Copyright (C) 2010-2015 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #ifndef CCACHE_SYSTEM_H
 #define CCACHE_SYSTEM_H
@@ -40,6 +38,7 @@
 #include <limits.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 extern char **environ;
 
+#ifndef ESTALE
+#define ESTALE -1
+#endif
+
 #if !HAVE_VSNPRINTF
   int rpl_vsnprintf(char *, size_t, const char *, va_list);
   #define vsnprintf rpl_vsnprintf
@@ -68,20 +71,4 @@ extern char **environ;
   #define asprintf rpl_asprintf
 #endif
 
-#ifdef HAVE_STDBOOL_H
-#  include <stdbool.h>
-#else
-#  ifndef HAVE__BOOL
-#    ifdef __cplusplus
-typedef bool _Bool;
-#    else
-#      define _Bool signed char
-#    endif
-#  endif
-#  define bool _Bool
-#  define false 0
-#  define true 1
-#  define __bool_true_false_are_defined 1
-#endif
-
-#endif /* CCACHE_SYSTEM_H */
+#endif // CCACHE_SYSTEM_H
diff --git a/test.sh b/test.sh
index 30aa3eb..2362b96 100755 (executable)
--- a/test.sh
+++ b/test.sh
 # this program; if not, write to the Free Software Foundation, Inc., 51
 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-unset CCACHE_BASEDIR
-unset CCACHE_CC
-unset CCACHE_COMPILERCHECK
-unset CCACHE_COMPRESS
-unset CCACHE_CPP2
-unset CCACHE_DIR
-unset CCACHE_DISABLE
-unset CCACHE_EXTENSION
-unset CCACHE_EXTRAFILES
-unset CCACHE_HARDLINK
-unset CCACHE_HASHDIR
-unset CCACHE_LOGFILE
-unset CCACHE_NLEVELS
-unset CCACHE_NODIRECT
-unset CCACHE_NOSTATS
-unset CCACHE_PATH
-unset CCACHE_PREFIX
-unset CCACHE_READONLY
-unset CCACHE_READONLY_DIRECT
-unset CCACHE_RECACHE
-unset CCACHE_SLOPPINESS
-unset CCACHE_TEMPDIR
-unset CCACHE_UMASK
-unset CCACHE_UNIFY
-unset GCC_COLORS
-
-# Many tests backdate files, which updates their ctimes.  In those tests, we
-# must ignore ctimes.  Might as well do so everywhere.
-default_sloppiness=include_file_ctime
-CCACHE_SLOPPINESS="$default_sloppiness"
-export CCACHE_SLOPPINESS
+green() {
+    printf "\033[1;32m$*\033[0;0m\n"
+}
+
+red() {
+    printf "\033[1;31m$*\033[0;0m\n"
+}
+
+bold() {
+    printf "\033[1;37m$*\033[0;0m\n"
+}
 
 test_failed() {
-    echo "SUITE: \"$testsuite\", TEST: \"$testname\" - $1"
+    echo
+    red FAILED
+    echo
+    echo "Test suite:     $(bold $CURRENT_SUITE)"
+    echo "Test case:      $(bold $CURRENT_TEST)"
+    echo "Failure reason: $(red $1)"
+    echo
+    echo "ccache -s:"
     $CCACHE -s
-    cd ..
-    echo TEST FAILED
+    echo
     echo "Test data and log file have been left in $TESTDIR"
     exit 1
 }
 
-randcode() {
-    outfile="$1"
-    nlines=$2
-    i=0
-    (
-    while [ $i -lt $nlines ]; do
-        echo "int foo$nlines$i(int x) { return x; }"
-        i=`expr $i + 1`
+generate_code() {
+    local nlines=$1
+    local outfile=$2
+
+    rm -f $outfile
+    for i in $(seq $nlines); do
+        echo "int foo_$i(int x) { return x; }" >>$outfile
     done
-    ) >> "$outfile"
 }
 
-getstat() {
-    stat="$1"
-    value=`$CCACHE -s | grep "$stat" | cut -c34-`
-    echo $value
+remove_cache() {
+    if [ -d $CCACHE_DIR ]; then
+        chmod -R +w $CCACHE_DIR
+        rm -rf $CCACHE_DIR
+    fi
+}
+
+clear_cache() {
+    $CCACHE -Cz >/dev/null
 }
 
-checkstat() {
-    stat="$1"
-    expected_value="$2"
-    value=`getstat "$stat"`
+sed_in_place() {
+    local expr=$1
+    shift
+
+    for file in $*; do
+        sed "$expr" $file >$file.sed
+        mv $file.sed $file
+    done
+}
+
+backdate() {
+    touch -t 199901010000 "$@"
+}
+
+expect_stat() {
+    local stat="$1"
+    local expected_value="$2"
+    local value="$(echo $($CCACHE -s | fgrep "$stat" | cut -c34-))"
+
     if [ "$expected_value" != "$value" ]; then
-        test_failed "Expected \"$stat\" to be $expected_value, got $value"
+        test_failed "Expected \"$stat\" to be $expected_value, actual $value"
     fi
 }
 
-compare_file() {
-    cmp -s "$1" "$2"
+expect_equal_files() {
+    if [ ! -e "$1" ]; then
+        test_failed "compare_files: $1 missing"
+    fi
+    if [ ! -e "$2" ]; then
+        test_failed "compare_files: $2 missing"
+    fi
+    if ! cmp -s "$1" "$2"; then
+        test_failed "compare_files:: $1 and $2 differ"
+    fi
+}
+
+expect_equal_object_files() {
+    if $HOST_OS_LINUX && $COMPILER_TYPE_CLANG; then
+        if ! which eu-elfcmp >/dev/null 2>&1; then
+            test_failed "Please install elfutils to get eu-elfcmp"
+        fi
+        eu-elfcmp -q "$1" "$2"
+    else
+        cmp -s "$1" "$2"
+    fi
     if [ $? -ne 0 ]; then
-        test_failed "Files differ: $1 != $2"
+        test_failed "Objects differ: $1 != $2"
     fi
 }
 
-checkfile() {
-    if [ ! -f $1 ]; then
-        test_failed "$1 not found"
+expect_file_content() {
+    local file="$1"
+    local content="$2"
+
+    if [ ! -f "$file" ]; then
+        test_failed "$file not found"
     fi
-    if [ "`cat $1`" != "$2" ]; then
-        test_failed "Bad content of $1.\nExpected: $2\nActual: `cat $1`"
+    if [ "$(cat $file)" != "$content" ]; then
+        test_failed "Bad content of $file.\nExpected: $content\nActual: $(cat $file)"
     fi
 }
 
-checkfilecount() {
-    expected=$1
-    pattern=$2
-    dir=$3
-    actual=`find $dir -name "$pattern" | wc -l`
+expect_file_count() {
+    local expected=$1
+    local pattern=$2
+    local dir=$3
+    local actual=`find $dir -name "$pattern" | wc -l`
     if [ $actual -ne $expected ]; then
         test_failed "Found $actual (expected $expected) $pattern files in $dir"
     fi
 }
 
-sed_in_place() {
-    expr=$1
-    shift
-    for file in $*; do
-        sed "$expr" > ${file}.sed < $file
-        mv ${file}.sed $file
-    done
+run_suite() {
+    local name=$1
+
+    CURRENT_SUITE=$name
+    UNCACHED_COMPILE=uncached_compile
+
+    cd $ABS_TESTDIR
+    rm -rf $ABS_TESTDIR/fixture
+
+    if type SUITE_${name}_PROBE >/dev/null 2>&1; then
+        mkdir $ABS_TESTDIR/probe
+        cd $ABS_TESTDIR/probe
+        local skip_reason="$(SUITE_${name}_PROBE)"
+        cd $ABS_TESTDIR
+        rm -rf $ABS_TESTDIR/probe
+        if [ -n "$skip_reason" ]; then
+            echo "Skipped test suite $name [$skip_reason]"
+            return
+        fi
+    fi
+
+    printf "Running test suite %s" "$(bold $name)"
+    SUITE_$name
+    echo
 }
 
-backdate() {
-    touch -t 199901010000 "$@"
+uncached_compile() {
+    # $COMPILER could be a masquerading system ccache, so make sure it's
+    # disabled:
+    CCACHE_DISABLE=1 $COMPILER "$@"
 }
 
-run_suite() {
-    rm -rf $CCACHE_DIR
-    CCACHE_NODIRECT=1
-    export CCACHE_NODIRECT
+TEST() {
+    CURRENT_TEST=$1
 
-    echo "starting testsuite $1"
-    testsuite=$1
+    unset CCACHE_BASEDIR
+    unset CCACHE_CC
+    unset CCACHE_COMMENTS
+    unset CCACHE_COMPILERCHECK
+    unset CCACHE_COMPRESS
+    unset CCACHE_CPP2
+    unset CCACHE_DIR
+    unset CCACHE_DISABLE
+    unset CCACHE_EXTENSION
+    unset CCACHE_EXTRAFILES
+    unset CCACHE_HARDLINK
+    unset CCACHE_IGNOREHEADERS
+    unset CCACHE_LIMIT_MULTIPLE
+    unset CCACHE_LOGFILE
+    unset CCACHE_NLEVELS
+    unset CCACHE_NOCPP2
+    unset CCACHE_NOHASHDIR
+    unset CCACHE_NOSTATS
+    unset CCACHE_PATH
+    unset CCACHE_PREFIX
+    unset CCACHE_PREFIX_CPP
+    unset CCACHE_READONLY
+    unset CCACHE_READONLY_DIRECT
+    unset CCACHE_RECACHE
+    unset CCACHE_SLOPPINESS
+    unset CCACHE_TEMPDIR
+    unset CCACHE_UMASK
+    unset CCACHE_UNIFY
+    unset GCC_COLORS
+
+    export CCACHE_CONFIGPATH=$ABS_TESTDIR/ccache.conf
+    export CCACHE_DETECT_SHEBANG=1
+    export CCACHE_DIR=$ABS_TESTDIR/.ccache
+    export CCACHE_LOGFILE=$ABS_TESTDIR/ccache.log
+    export CCACHE_NODIRECT=1
+
+    # Many tests backdate files, which updates their ctimes. In those tests, we
+    # must ignore ctimes. Might as well do so everywhere.
+    DEFAULT_SLOPPINESS=include_file_ctime
+    export CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS"
 
-    ${1}_suite
+    CCACHE_COMPILE="$CCACHE $COMPILER"
 
-    testname="the tmp directory should be empty"
-    if [ -d $CCACHE_DIR/tmp ] && [ "`find $CCACHE_DIR/tmp -type f | wc -l`" -gt 0 ]; then
-        test_failed "$CCACHE_DIR/tmp is not empty"
+    if $VERBOSE; then
+        printf "\n  %s" $CURRENT_TEST
+    else
+        printf .
+    fi
+
+    cd /
+    remove_cache
+    rm -rf $ABS_TESTDIR/run
+    mkdir $ABS_TESTDIR/run
+    cd $ABS_TESTDIR/run
+    if type SUITE_${name}_SETUP >/dev/null 2>&1; then
+        SUITE_${name}_SETUP
     fi
 }
 
+# =============================================================================
+
 base_tests() {
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat 'files in cache' 0
-
-    j=1
-    rm -f *.c
-    while [ $j -lt 32 ]; do
-        randcode test$j.c $j
-        j=`expr $j + 1`
-    done
+    # -------------------------------------------------------------------------
+    TEST "Base case"
 
-    CCACHE_DISABLE=1 $COMPILER -c -o reference_test1.o test1.c
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
 
-    testname="BASIC"
     $CCACHE_COMPILE -c test1.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkstat 'files in cache' 1
-    compare_file reference_test1.o test1.o
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+    expect_equal_object_files reference_test1.o test1.o
 
-    testname="BASIC2"
     $CCACHE_COMPILE -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-    checkstat 'files in cache' 1
-    compare_file reference_test1.o test1.o
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+    expect_equal_object_files reference_test1.o test1.o
+
+    # -------------------------------------------------------------------------
+    TEST "Debug option"
 
-    testname="debug"
     $CCACHE_COMPILE -c test1.c -g
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
-    checkstat 'files in cache' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
 
-    testname="debug2"
     $CCACHE_COMPILE -c test1.c -g
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 2
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c -g
+    expect_equal_object_files reference_test1.o reference_test1.o
+
+    # -------------------------------------------------------------------------
+    TEST "Output option"
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="output"
     $CCACHE_COMPILE -c test1.c -o foo.o
-    checkstat 'cache hit (preprocessed)' 3
-    checkstat 'cache miss' 2
-    compare_file reference_test1.o foo.o
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
+    expect_equal_object_files reference_test1.o foo.o
+
+    # -------------------------------------------------------------------------
+    TEST "Called for link"
+
+    $CCACHE_COMPILE test1.c -o test 2>/dev/null
+    expect_stat 'called for link' 1
 
-    testname="link"
-    $CCACHE_COMPILE test1.c -o test 2> /dev/null
-    checkstat 'called for link' 1
+    $CCACHE_COMPILE -c test1.c
+    $CCACHE_COMPILE test1.o -o test 2>/dev/null
+    expect_stat 'called for link' 2
+
+    # -------------------------------------------------------------------------
+    TEST "No input file"
+
+    $CCACHE_COMPILE -c foo.c 2>/dev/null
+    expect_stat 'no input file' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Called for preprocessing"
 
-    testname="linkobj"
-    $CCACHE_COMPILE foo.o -o test 2> /dev/null
-    checkstat 'called for link' 2
+    $CCACHE_COMPILE -E -c test1.c >/dev/null 2>&1
+    expect_stat 'called for preprocessing' 1
 
-    testname="preprocessing"
-    $CCACHE_COMPILE -E -c test1.c > /dev/null 2>&1
-    checkstat 'called for preprocessing' 1
+    # -------------------------------------------------------------------------
+    TEST "Multiple source files"
 
-    testname="multiple"
+    touch test2.c
     $CCACHE_COMPILE -c test1.c test2.c
-    checkstat 'multiple source files' 1
+    expect_stat 'multiple source files' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Couldn't find the compiler"
+
+    $CCACHE blahblah -c test1.c 2>/dev/null
+    expect_stat "couldn't find the compiler" 1
+
+    # -------------------------------------------------------------------------
+    TEST "Bad compiler arguments"
 
-    testname="find"
-    $CCACHE blahblah -c test1.c 2> /dev/null
-    checkstat "couldn't find the compiler" 1
+    $CCACHE_COMPILE -c test1.c -I 2>/dev/null
+    expect_stat 'bad compiler arguments' 1
 
-    testname="bad"
-    $CCACHE_COMPILE -c test1.c -I 2> /dev/null
-    checkstat 'bad compiler arguments' 1
+    # -------------------------------------------------------------------------
+    TEST "Unsupported source language"
 
-    testname="unsupported source language"
     ln -f test1.c test1.ccc
-    $CCACHE_COMPILE -c test1.ccc 2> /dev/null
-    checkstat 'unsupported source language' 1
+    $CCACHE_COMPILE -c test1.ccc 2>/dev/null
+    expect_stat 'unsupported source language' 1
 
-    testname="unsupported"
-    $CCACHE_COMPILE -M foo -c test1.c > /dev/null 2>&1
-    checkstat 'unsupported compiler option' 1
+    # -------------------------------------------------------------------------
+    TEST "Unsupported compiler option"
 
-    testname="stdout"
-    $CCACHE echo foo -c test1.c > /dev/null
-    checkstat 'compiler produced stdout' 1
+    $CCACHE_COMPILE -M foo -c test1.c >/dev/null 2>&1
+    expect_stat 'unsupported compiler option' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Compiler produced stdout"
+
+    $CCACHE echo foo -c test1.c >/dev/null
+    expect_stat 'compiler produced stdout' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Output to a non-regular file"
 
-    testname="non-regular"
     mkdir testd
-    $CCACHE_COMPILE -o testd -c test1.c > /dev/null 2>&1
-    rmdir testd > /dev/null 2>&1
-    checkstat 'output to a non-regular file' 1
-
-    testname="no-input"
-    $CCACHE_COMPILE -c -O2 2> /dev/null
-    checkstat 'no input file' 1
-
-    testname="CCACHE_DISABLE"
-    mv $CCACHE_DIR $CCACHE_DIR.saved
-    saved_config_path=$CCACHE_CONFIGPATH
-    unset CCACHE_CONFIGPATH
-    CCACHE_DISABLE=1 $CCACHE_COMPILE -c test1.c 2> /dev/null
+    $CCACHE_COMPILE -o testd -c test1.c >/dev/null 2>&1
+    rmdir testd >/dev/null 2>&1
+    expect_stat 'output to a non-regular file' 1
+
+    # -------------------------------------------------------------------------
+    TEST "No input file"
+
+    $CCACHE_COMPILE -c -O2 2>/dev/null
+    expect_stat 'no input file' 1
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_DISABLE"
+
+    CCACHE_DISABLE=1 $CCACHE_COMPILE -c test1.c 2>/dev/null
     if [ -d $CCACHE_DIR ]; then
         test_failed "$CCACHE_DIR created despite CCACHE_DISABLE being set"
     fi
-    CCACHE_CONFIGPATH=$saved_config_path
-    mv $CCACHE_DIR.saved $CCACHE_DIR
-    checkstat 'cache hit (preprocessed)' 3
-    $CCACHE_COMPILE -c test1.c
-    checkstat 'cache hit (preprocessed)' 4
 
-    testname="CCACHE_CPP2"
-    CCACHE_CPP2=1 $CCACHE_COMPILE -c test1.c -O -O
-    checkstat 'cache hit (preprocessed)' 4
-    checkstat 'cache miss' 3
-    CCACHE_DISABLE=1 $COMPILER -c test1.c -o reference_test1.o -O -O
-    compare_file reference_test1.o test1.o
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_COMMENTS"
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
+
+    mv test1.c test1-saved.c
+    echo '// initial comment' >test1.c
+    cat test1-saved.c >>test1.c
+    CCACHE_COMMENTS=1 $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    echo '// different comment' >test1.c
+    cat test1-saved.c >>test1.c
+    CCACHE_COMMENTS=1 $CCACHE_COMPILE -c test1.c
+    mv test1-saved.c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
+    expect_equal_object_files reference_test1.o test1.o
 
-    CCACHE_CPP2=1 $CCACHE_COMPILE -c test1.c -O -O
-    checkstat 'cache hit (preprocessed)' 5
-    checkstat 'cache miss' 3
-    compare_file reference_test1.o test1.o
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_NOSTATS"
 
-    testname="CCACHE_NOSTATS"
     CCACHE_NOSTATS=1 $CCACHE_COMPILE -c test1.c -O -O
-    checkstat 'cache hit (preprocessed)' 5
-    checkstat 'cache miss' 3
-
-    testname="CCACHE_RECACHE"
-    CCACHE_RECACHE=1 $CCACHE_COMPILE -c test1.c -O -O
-    checkstat 'cache hit (preprocessed)' 5
-    checkstat 'cache miss' 4
-    compare_file reference_test1.o test1.o
-
-    # strictly speaking should be 4 - RECACHE causes a double counting!
-    checkstat 'files in cache' 4
-    $CCACHE -c > /dev/null
-    checkstat 'files in cache' 4
-
-    testname="CCACHE_HASHDIR"
-    CCACHE_HASHDIR=1 $CCACHE_COMPILE -c test1.c -O -O
-    checkstat 'cache hit (preprocessed)' 5
-    checkstat 'cache miss' 5
-    compare_file reference_test1.o test1.o
-
-    CCACHE_HASHDIR=1 $CCACHE_COMPILE -c test1.c -O -O
-    checkstat 'cache hit (preprocessed)' 6
-    checkstat 'cache miss' 5
-    checkstat 'files in cache' 5
-    compare_file reference_test1.o test1.o
-
-    testname="comments"
-    echo '/* a silly comment */' > test1-comment.c
-    cat test1.c >> test1-comment.c
-    $CCACHE_COMPILE -c test1-comment.c
-    rm -f test1-comment*
-    checkstat 'cache hit (preprocessed)' 6
-    checkstat 'cache miss' 6
-
-    testname="CCACHE_UNIFY"
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_RECACHE"
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_RECACHE=1 $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
+    expect_equal_object_files reference_test1.o test1.o
+
+    # CCACHE_RECACHE replaces the object file, so the statistics counter will
+    # be off-by-one until next cleanup.
+    expect_stat 'files in cache' 2
+    $CCACHE -c >/dev/null
+    expect_stat 'files in cache' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Directory is hashed if using -g"
+
+    mkdir dir1 dir2
+    cp test1.c dir1
+    cp test1.c dir2
+
+    cd dir1
+    $CCACHE_COMPILE -c test1.c -g
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    $CCACHE_COMPILE -c test1.c -g
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    cd ../dir2
+    $CCACHE_COMPILE -c test1.c -g
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
+    $CCACHE_COMPILE -c test1.c -g
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "Directory is not hashed if not using -g"
+
+    mkdir dir1 dir2
+    cp test1.c dir1
+    cp test1.c dir2
+
+    cd dir1
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    cd ../dir2
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_NOHASHDIR"
+
+    mkdir dir1 dir2
+    cp test1.c dir1
+    cp test1.c dir2
+
+    cd dir1
+    CCACHE_NOHASHDIR=1 $CCACHE_COMPILE -c test1.c -g
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    CCACHE_NOHASHDIR=1 $CCACHE_COMPILE -c test1.c -g
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    cd ../dir2
+    CCACHE_NOHASHDIR=1 $CCACHE_COMPILE -c test1.c -g
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_UNIFY"
+
+    echo '// a silly comment' >>test1.c
     CCACHE_UNIFY=1 $CCACHE_COMPILE -c test1.c
-    checkstat 'cache hit (preprocessed)' 6
-    checkstat 'cache miss' 7
-    mv test1.c test1-saved.c
-    echo '/* another comment */' > test1.c
-    cat test1-saved.c >> test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    echo '// another silly comment' >>test1.c
     CCACHE_UNIFY=1 $CCACHE_COMPILE -c test1.c
-    mv test1-saved.c test1.c
-    checkstat 'cache hit (preprocessed)' 7
-    checkstat 'cache miss' 7
-    CCACHE_DISABLE=1 $COMPILER -c test1.c -o reference_test1.o
-    compare_file reference_test1.o test1.o
-
-    testname="cache-size"
-    for f in *.c; do
-        $CCACHE_COMPILE -c $f
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
+    expect_equal_object_files reference_test1.o test1.o
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_NLEVELS"
+
+    CCACHE_NLEVELS=4 $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+
+    CCACHE_NLEVELS=4 $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+
+    # Directories in $CCACHE_DIR:
+    # - .
+    # - tmp
+    # - a
+    # - a/b
+    # - a/b/c
+    # - a/b/c/d
+    actual_dirs=$(find $CCACHE_DIR -type d | wc -l)
+    expected_dirs=6
+    if [ $actual_dirs -ne $expected_dirs ]; then
+        test_failed "Expected $expected_dirs directories, found $actual_dirs"
+    fi
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_EXTRAFILES"
+
+    echo a >a
+    echo b >b
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
+
+    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 2
+
+    echo b2 >b
+
+    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 3
+
+    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 3
+    expect_stat 'cache miss' 3
+
+    CCACHE_EXTRAFILES="doesntexist" $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 3
+    expect_stat 'cache miss' 3
+    expect_stat 'error hashing extra file' 1
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_PREFIX"
+
+    cat <<'EOF' >prefix-a
+#!/bin/sh
+echo a >>prefix.result
+exec "$@"
+EOF
+    cat <<'EOF' >prefix-b
+#!/bin/sh
+echo b >>prefix.result
+exec "$@"
+EOF
+    chmod +x prefix-a prefix-b
+    cat <<'EOF' >file.c
+int foo;
+EOF
+    PATH=.:$PATH CCACHE_PREFIX="prefix-a prefix-b" $CCACHE_COMPILE -c file.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_file_content prefix.result "a
+b"
+
+    PATH=.:$PATH CCACHE_PREFIX="prefix-a prefix-b" $CCACHE_COMPILE -c file.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_file_content prefix.result "a
+b"
+
+    rm -f prefix.result
+    PATH=.:$PATH CCACHE_PREFIX_CPP="prefix-a prefix-b" $CCACHE_COMPILE -c file.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 1
+    expect_file_content prefix.result "a
+b"
+
+    # -------------------------------------------------------------------------
+    TEST "Files in cache"
+
+    for i in $(seq 32); do
+        generate_code $i test$i.c
+        $CCACHE_COMPILE -c test$i.c
     done
-    checkstat 'cache hit (preprocessed)' 8
-    checkstat 'cache miss' 37
-    checkstat 'files in cache' 37
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 32
+    expect_stat 'files in cache' 32
 
-    $CCACHE -C >/dev/null
+    # -------------------------------------------------------------------------
+    TEST "Called for preprocessing"
 
-    testname="cpp call"
-    $CCACHE_COMPILE -c test1.c -E > test1.i
-    checkstat 'cache hit (preprocessed)' 8
-    checkstat 'cache miss' 37
+    $CCACHE_COMPILE -c test1.c -E >test1.i
+    expect_stat 'called for preprocessing' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Direct .i compile"
 
-    testname="direct .i compile"
     $CCACHE_COMPILE -c test1.c
-    checkstat 'cache hit (preprocessed)' 8
-    checkstat 'cache miss' 38
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
+    $UNCACHED_COMPILE -c test1.c -E >test1.i
     $CCACHE_COMPILE -c test1.i
-    checkstat 'cache hit (preprocessed)' 9
-    checkstat 'cache miss' 38
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
 
-    $CCACHE_COMPILE -c test1.i
-    checkstat 'cache hit (preprocessed)' 10
-    checkstat 'cache miss' 38
+    # -------------------------------------------------------------------------
+    TEST "-x c"
+
+    ln -f test1.c test1.ccc
 
-    testname="-x c"
     $CCACHE_COMPILE -x c -c test1.ccc
-    checkstat 'cache hit (preprocessed)' 10
-    checkstat 'cache miss' 39
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     $CCACHE_COMPILE -x c -c test1.ccc
-    checkstat 'cache hit (preprocessed)' 11
-    checkstat 'cache miss' 39
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "-xc"
+
+    ln -f test1.c test1.ccc
+
+    $CCACHE_COMPILE -xc -c test1.ccc
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="-xc"
     $CCACHE_COMPILE -xc -c test1.ccc
-    checkstat 'cache hit (preprocessed)' 12
-    checkstat 'cache miss' 39
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "-x none"
+
+    $CCACHE_COMPILE -x assembler -x none -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="-x none"
     $CCACHE_COMPILE -x assembler -x none -c test1.c
-    checkstat 'cache hit (preprocessed)' 13
-    checkstat 'cache miss' 39
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "-x unknown"
 
-    testname="-x unknown"
     $CCACHE_COMPILE -x unknown -c test1.c 2>/dev/null
-    checkstat 'cache hit (preprocessed)' 13
-    checkstat 'cache miss' 39
-    checkstat 'unsupported source language' 2
-
-    testname="-D not hashed"
-    $CCACHE_COMPILE -DNOT_AFFECTING=1 -c test1.c 2>/dev/null
-    checkstat 'cache hit (preprocessed)' 14
-    checkstat 'cache miss' 39
-
-    if [ -x /usr/bin/printf ]; then
-        /usr/bin/printf '#include <wchar.h>\nwchar_t foo[] = L"\xbf";\n' >latin1.c
-        if CCACHE_DISABLE=1 $COMPILER -c -finput-charset=latin1 latin1.c >/dev/null 2>&1; then
-            testname="-finput-charset"
-            CCACHE_CPP2=1 $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
-            checkstat 'cache hit (preprocessed)' 14
-            checkstat 'cache miss' 40
-            CCACHE_CPP2=1 $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
-            checkstat 'cache hit (preprocessed)' 15
-            checkstat 'cache miss' 40
-            $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
-            checkstat 'cache hit (preprocessed)' 15
-            checkstat 'cache miss' 41
-            $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
-            checkstat 'cache hit (preprocessed)' 16
-            checkstat 'cache miss' 41
-        fi
-    fi
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat 'unsupported source language' 1
+
+    # -------------------------------------------------------------------------
+    TEST "-D not hashed"
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -DNOT_AFFECTING=1 -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "-S"
+
+    $CCACHE_COMPILE -S test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -S test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c test1.s
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
+
+    $CCACHE_COMPILE -c test1.s
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_PATH"
 
-    testname="override path"
-    $CCACHE -Cz >/dev/null
     override_path=`pwd`/override_path
-    rm -rf $override_path
     mkdir $override_path
     cat >$override_path/cc <<EOF
 #!/bin/sh
@@ -401,86 +712,125 @@ EOF
         test_failed "CCACHE_PATH had no effect"
     fi
 
-    testname="compilercheck=mtime"
-    $CCACHE -Cz >/dev/null
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_COMPILERCHECK=mtime"
+
     cat >compiler.sh <<EOF
 #!/bin/sh
-CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
-export CCACHE_DISABLE
+export CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
 exec $COMPILER "\$@"
 # A comment
 EOF
     chmod +x compiler.sh
     backdate compiler.sh
     CCACHE_COMPILERCHECK=mtime $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     sed_in_place 's/comment/yoghurt/' compiler.sh # Don't change the size
     chmod +x compiler.sh
-    backdate compiler.sh
+    backdate compiler.sh # Don't change the timestamp
+
     CCACHE_COMPILERCHECK=mtime $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
     touch compiler.sh
     CCACHE_COMPILERCHECK=mtime $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_COMPILERCHECK=content"
+
+    cat >compiler.sh <<EOF
+#!/bin/sh
+export CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
+exec $COMPILER "\$@"
+EOF
+    chmod +x compiler.sh
 
-    testname="compilercheck=content"
-    $CCACHE -z >/dev/null
     CCACHE_COMPILERCHECK=content $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    backdate compiler.sh
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     CCACHE_COMPILERCHECK=content $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
     echo "# Compiler upgrade" >>compiler.sh
-    backdate compiler.sh
+
     CCACHE_COMPILERCHECK=content $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_COMPILERCHECK=none"
+
+    cat >compiler.sh <<EOF
+#!/bin/sh
+export CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
+exec $COMPILER "\$@"
+EOF
+    chmod +x compiler.sh
 
-    testname="compilercheck=none"
-    $CCACHE -z >/dev/null
-    backdate compiler.sh
     CCACHE_COMPILERCHECK=none $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     CCACHE_COMPILERCHECK=none $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
     echo "# Compiler upgrade" >>compiler.sh
     CCACHE_COMPILERCHECK=none $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_COMPILERCHECK=string"
+
+    cat >compiler.sh <<EOF
+#!/bin/sh
+export CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
+exec $COMPILER "\$@"
+EOF
+    chmod +x compiler.sh
 
-    testname="compilercheck=string"
-    $CCACHE -z >/dev/null
-    backdate compiler.sh
     CCACHE_COMPILERCHECK=string:foo $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     CCACHE_COMPILERCHECK=string:foo $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
     CCACHE_COMPILERCHECK=string:bar $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
+
     CCACHE_COMPILERCHECK=string:bar $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 2
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_COMPILERCHECK=command"
+
+    cat >compiler.sh <<EOF
+#!/bin/sh
+export CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
+exec $COMPILER "\$@"
+EOF
+    chmod +x compiler.sh
 
-    testname="compilercheck=command"
-    $CCACHE -z >/dev/null
-    backdate compiler.sh
     CCACHE_COMPILERCHECK='echo %compiler%' $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     echo "# Compiler upgrade" >>compiler.sh
     CCACHE_COMPILERCHECK="echo ./compiler.sh" $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
     cat <<EOF >foobar.sh
 #!/bin/sh
 echo foo
@@ -488,30 +838,41 @@ echo bar
 EOF
     chmod +x foobar.sh
     CCACHE_COMPILERCHECK='./foobar.sh' $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
+
     CCACHE_COMPILERCHECK='echo foo; echo bar' $CCACHE ./compiler.sh -c test1.c
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 2
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_COMPILERCHECK=unknown_command"
+
+    cat >compiler.sh <<EOF
+#!/bin/sh
+export CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
+exec $COMPILER "\$@"
+EOF
+    chmod +x compiler.sh
 
-    testname="compilercheck=unknown_command"
-    $CCACHE -z >/dev/null
-    backdate compiler.sh
     CCACHE_COMPILERCHECK="unknown_command" $CCACHE ./compiler.sh -c test1.c 2>/dev/null
     if [ "$?" -eq 0 ]; then
         test_failed "Expected failure running unknown_command to verify compiler but was success"
     fi
-    checkstat 'compiler check failed' 1
+    expect_stat 'compiler check failed' 1
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_RECACHE should remove previous .stderr"
 
-    testname="recache should remove previous .stderr"
-    $CCACHE -Cz >/dev/null
     $CCACHE_COMPILE -c test1.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     num=`find $CCACHE_DIR -name '*.stderr' | wc -l`
     if [ $num -ne 0 ]; then
         test_failed "$num stderr files found, expected 0 (#1)"
     fi
+
     obj_file=`find $CCACHE_DIR -name '*.o'`
     stderr_file=`echo $obj_file | sed 's/..$/.stderr/'`
     echo "Warning: foo" >$stderr_file
@@ -521,234 +882,473 @@ EOF
         test_failed "$num stderr files found, expected 0 (#2)"
     fi
 
-    testname="no object file"
-    $CCACHE -Cz >/dev/null
+    # -------------------------------------------------------------------------
+    TEST "No object file"
+
     cat <<'EOF' >test_no_obj.c
 int test_no_obj;
 EOF
     cat <<'EOF' >prefix-remove.sh
 #!/bin/sh
 "$@"
+[ x$2 = x-fcolor-diagnostics ] && shift
+[ x$2 = x-fdiagnostics-color ] && shift
+[ x$2 = x-std=gnu99 ] && shift
 [ x$3 = x-o ] && rm $4
 EOF
     chmod +x prefix-remove.sh
     CCACHE_PREFIX=`pwd`/prefix-remove.sh $CCACHE_COMPILE -c test_no_obj.c
-    checkstat 'compiler produced no output' 1
+    expect_stat 'compiler produced no output' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Empty object file"
 
-    testname="empty object file"
     cat <<'EOF' >test_empty_obj.c
 int test_empty_obj;
 EOF
     cat <<'EOF' >prefix-empty.sh
 #!/bin/sh
 "$@"
+[ x$2 = x-fcolor-diagnostics ] && shift
+[ x$2 = x-fdiagnostics-color ] && shift
+[ x$2 = x-std=gnu99 ] && shift
 [ x$3 = x-o ] && cp /dev/null $4
 EOF
     chmod +x prefix-empty.sh
     CCACHE_PREFIX=`pwd`/prefix-empty.sh $CCACHE_COMPILE -c test_empty_obj.c
-    checkstat 'compiler produced empty output' 1
+    expect_stat 'compiler produced empty output' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Caching stderr"
 
-    testname="stderr-files"
-    $CCACHE -Cz >/dev/null
-    num=`find $CCACHE_DIR -name '*.stderr' | wc -l`
-    if [ $num -ne 0 ]; then
-        test_failed "$num stderr files found, expected 0"
-    fi
     cat <<EOF >stderr.c
 int stderr(void)
 {
-       /* Trigger warning by having no return statement. */
+  // Trigger warning by having no return statement.
 }
 EOF
-    checkstat 'files in cache' 0
     $CCACHE_COMPILE -Wall -W -c stderr.c 2>/dev/null
     num=`find $CCACHE_DIR -name '*.stderr' | wc -l`
     if [ $num -ne 1 ]; then
         test_failed "$num stderr files found, expected 1"
     fi
-    checkstat 'files in cache' 2
-
-    testname="zero-stats"
-    $CCACHE -z > /dev/null
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat 'files in cache' 2
-
-    testname="clear"
-    $CCACHE -C > /dev/null
-    checkstat 'files in cache' 0
-
-    # the profile options do not seem to work correctly with clang or gcc-llvm
-    # on darwin machines
-    darwin_llvm=0
-    if [ $HOST_OS_APPLE -eq 1 ] && [ $COMPILER_USES_LLVM -eq 1 ]; then
-        darwin_llvm=1
-    fi
+    expect_stat 'files in cache' 2
 
-    $COMPILER -c -fprofile-generate test1.c 2>/dev/null
-    if [ $? -eq 0 ] && [ $darwin_llvm -eq 0 ]; then
-        testname="profile-generate"
-        $CCACHE -Cz > /dev/null
-        $CCACHE_COMPILE -c -fprofile-generate test1.c
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 1
-        checkstat 'files in cache' 1
-        $CCACHE_COMPILE -c -fprofile-generate test1.c
-        checkstat 'cache hit (preprocessed)' 1
-        checkstat 'cache miss' 1
-        checkstat 'files in cache' 1
-
-        testname="profile-arcs"
-        $CCACHE_COMPILE -c -fprofile-arcs test1.c
-        checkstat 'cache hit (preprocessed)' 1
-        checkstat 'cache miss' 2
-        checkstat 'files in cache' 2
-        $CCACHE_COMPILE -c -fprofile-arcs test1.c
-        checkstat 'cache hit (preprocessed)' 2
-        checkstat 'cache miss' 2
-        checkstat 'files in cache' 2
-
-        testname="profile-use"
-        $CCACHE_COMPILE -c -fprofile-use test1.c 2>/dev/null
-        checkstat 'cache hit (preprocessed)' 2
-        checkstat 'cache miss' 3
-        $CCACHE_COMPILE -c -fprofile-use test1.c 2>/dev/null
-        checkstat 'cache hit (preprocessed)' 3
-        checkstat 'cache miss' 3
-    fi
+    # -------------------------------------------------------------------------
+    TEST "--zero-stats"
+
+    $CCACHE_COMPILE -c test1.c
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+
+    $CCACHE -z >/dev/null
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat 'files in cache' 1
+
+    # -------------------------------------------------------------------------
+    TEST "--clear"
+
+    $CCACHE_COMPILE -c test1.c
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+
+    $CCACHE -C >/dev/null
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 0
+
+    # -------------------------------------------------------------------------
+    TEST "-P"
+
+    # Check that -P disables ccache. (-P removes preprocessor information in
+    # such a way that the object file from compiling the preprocessed file will
+    # not be equal to the object file produced when compiling without ccache.)
+
+    $CCACHE_COMPILE -c -P test1.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat 'unsupported compiler option' 1
+
+    # -------------------------------------------------------------------------
+    TEST "-Wp,-P"
 
-    ##################################################################
     # Check that -Wp,-P disables ccache. (-P removes preprocessor information
     # in such a way that the object file from compiling the preprocessed file
     # will not be equal to the object file produced when compiling without
     # ccache.)
-    testname="-Wp,-P"
-    $CCACHE -Cz >/dev/null
-    $CCACHE_COMPILE -c -Wp,-P test1.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat 'unsupported compiler option' 1
+
     $CCACHE_COMPILE -c -Wp,-P test1.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat 'unsupported compiler option' 2
-    $CCACHE_COMPILE -c -Wp,-DFOO,-P,-DGOO test1.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat 'unsupported compiler option' 3
-    $CCACHE_COMPILE -c -Wp,-DFOO,-P,-DGOO test1.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat 'unsupported compiler option' 4
-
-    ##################################################################
-
-    if [ $COMPILER_TYPE_CLANG -eq 1 ]; then
-        testname="serialize-diagnostics"
-        $CCACHE -Cz > /dev/null
-        $COMPILER -c --serialize-diagnostics expected.dia test1.c 2> /dev/null
-        # Run with CCACHE_CPP2 to ensure the same diagnostics output as above
-        CCACHE_CPP2=1 $CCACHE_COMPILE -c --serialize-diagnostics test.dia test1.c 2> /dev/null
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 1
-        checkstat 'files in cache' 2
-        compare_file expected.dia test.dia
-        rm -f test.dia
-        CCACHE_CPP2=1 $CCACHE_COMPILE -c --serialize-diagnostics test.dia test1.c 2> /dev/null
-        checkstat 'cache hit (preprocessed)' 1
-        checkstat 'cache miss' 1
-        checkstat 'files in cache' 2
-        compare_file expected.dia test.dia
-
-        rm -f test.dia
-        rm -f expected.dia
-
-        testname="serialize-diagnostics-compile-failed"
-        $CCACHE -Cz > /dev/null
-        echo "bad source" >error.c
-        $COMPILER -c --serialize-diagnostics expected.dia error.c 2> /dev/null
-        if [ $? -eq 0 ]; then
-            test_failed "expected an error compiling error.c"
-        fi
-        CCACHE_CPP2=1 $CCACHE_COMPILE -c --serialize-diagnostics test.dia error.c 2> /dev/null
-        checkstat 'compile failed' 1
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 0
-        checkstat 'files in cache' 0
-        compare_file expected.dia test.dia
-        rm -f test.dia
-        CCACHE_CPP2=1 $CCACHE_COMPILE -c --serialize-diagnostics test.dia error.c 2> /dev/null
-        checkstat 'compile failed' 2
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 0
-        checkstat 'files in cache' 0
-        compare_file expected.dia test.dia
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat 'unsupported compiler option' 1
+
+    $CCACHE_COMPILE -c -Wp,-P,-DFOO test1.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat 'unsupported compiler option' 2
+
+    $CCACHE_COMPILE -c -Wp,-DFOO,-P test1.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat 'unsupported compiler option' 3
+
+    # -------------------------------------------------------------------------
+    TEST "-Wp,-D"
+
+    $CCACHE_COMPILE -c -Wp,-DFOO test1.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c -DFOO test1.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Buggy GCC 6 cpp"
+
+    cat >buggy-cpp <<EOF
+#!/bin/sh
+export CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
+if echo "\$*" | grep -- -D >/dev/null; then
+  $COMPILER "\$@"
+else
+  # Mistreat the preprocessor output in the same way as GCC 6 does.
+  $COMPILER "\$@" |
+    sed -e '/^# 1 "<command-line>"\$/ a\\
+# 31 "<command-line>"' \\
+        -e 's/^# 1 "<command-line>" 2\$/# 32 "<command-line>" 2/'
+fi
+exit 0
+EOF
+    cat <<'EOF' >file.c
+int foo;
+EOF
+    chmod +x buggy-cpp
+
+    $CCACHE ./buggy-cpp -c file.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE ./buggy-cpp -DNOT_AFFECTING=1 -c file.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Symlink to source directory"
+
+    mkdir dir
+    cd dir
+    mkdir -p d1/d2
+    echo '#define A "OK"' >d1/h.h
+    cat <<EOF >d1/d2/c.c
+#include <stdio.h>
+#include "../h.h"
+int main() { printf("%s\n", A); }
+EOF
+    echo '#define A "BUG"' >h.h
+    ln -s d1/d2 d3
+
+    CCACHE_BASEDIR=/ $CCACHE_COMPILE -c $PWD/d3/c.c
+    $UNCACHED_COMPILE c.o -o c
+    if [ "$(./c)" != OK ]; then
+        test_failed "Incorrect header file used"
     fi
 
-    ##################################################################
+    # -------------------------------------------------------------------------
+    TEST "Symlink to source file"
 
-    rm -f test1.c
+    mkdir dir
+    cd dir
+    mkdir d
+    echo '#define A "BUG"' >d/h.h
+    cat <<EOF >d/c.c
+#include <stdio.h>
+#include "h.h"
+int main() { printf("%s\n", A); }
+EOF
+    echo '#define A "OK"' >h.h
+    ln -s d/c.c c.c
+
+    CCACHE_BASEDIR=/ $CCACHE_COMPILE -c $PWD/c.c
+    $UNCACHED_COMPILE c.o -o c
+    if [ "$(./c)" != OK ]; then
+        test_failed "Incorrect header file used"
+    fi
 }
 
-base_suite() {
-    CCACHE_COMPILE="$CCACHE $COMPILER"
+# =============================================================================
+
+SUITE_base_SETUP() {
+    generate_code 1 test1.c
+}
+
+SUITE_base() {
     base_tests
 }
 
-link_suite() {
-    if [ `dirname $COMPILER` = . ]; then
-        ln -s "$CCACHE" $COMPILER
-        CCACHE_COMPILE="./$COMPILER"
-        base_tests
-        rm -f $COMPILER
-    else
-        echo "Compiler ($COMPILER) not taken from PATH -- not running link test"
-    fi
+# =============================================================================
+
+SUITE_nocpp2_SETUP() {
+    export CCACHE_NOCPP2=1
+    generate_code 1 test1.c
 }
 
-hardlink_suite() {
-    CCACHE_COMPILE="$CCACHE $COMPILER"
-    CCACHE_HARDLINK=1
-    export CCACHE_HARDLINK
+SUITE_nocpp2() {
     base_tests
-    unset CCACHE_HARDLINK
 }
 
-cpp2_suite() {
-    CCACHE_COMPILE="$CCACHE $COMPILER"
-    CCACHE_CPP2=1
-    export CCACHE_CPP2
-    base_tests
-    unset CCACHE_CPP2
+# =============================================================================
+
+SUITE_multi_arch_PROBE() {
+    if ! $HOST_OS_APPLE; then
+        echo "multiple -arch options not supported on $(uname -s)"
+        return
+    fi
 }
 
-nlevels4_suite() {
-    CCACHE_COMPILE="$CCACHE $COMPILER"
-    CCACHE_NLEVELS=4
-    export CCACHE_NLEVELS
-    base_tests
-    unset CCACHE_NLEVELS
+SUITE_multi_arch() {
+    # -------------------------------------------------------------------------
+    TEST "cache hit"
+
+    generate_code 1 test1.c
+
+    $CCACHE_COMPILE -arch i386 -arch x86_64 -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -arch i386 -arch x86_64 -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+}
+
+# =============================================================================
+
+SUITE_serialize_diagnostics_PROBE() {
+    touch test.c
+    if ! $UNCACHED_COMPILE -c --serialize-diagnostics \
+         test1.dia test.c 2>/dev/null; then
+        echo "--serialize-diagnostics not supported by compiler"
+    fi
+}
+
+SUITE_serialize_diagnostics_SETUP() {
+    generate_code 1 test1.c
+}
+
+SUITE_serialize_diagnostics() {
+    # -------------------------------------------------------------------------
+    TEST "Compile OK"
+
+    $UNCACHED_COMPILE -c --serialize-diagnostics expected.dia test1.c
+
+    $CCACHE_COMPILE -c --serialize-diagnostics test.dia test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 2
+    expect_equal_files expected.dia test.dia
+
+    rm test.dia
+
+    $CCACHE_COMPILE -c --serialize-diagnostics test.dia test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 2
+    expect_equal_files expected.dia test.dia
+
+    # -------------------------------------------------------------------------
+    TEST "Compile failed"
+
+    echo "bad source" >error.c
+    if $UNCACHED_COMPILE -c --serialize-diagnostics expected.dia error.c 2>expected.stderr; then
+        test_failed "Expected an error compiling error.c"
+    fi
+
+    $CCACHE_COMPILE -c --serialize-diagnostics test.dia error.c 2>test.stderr
+    expect_stat 'compile failed' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat 'files in cache' 0
+    expect_equal_files expected.dia test.dia
+    expect_equal_files expected.stderr test.stderr
+
+    # -------------------------------------------------------------------------
+    TEST "--serialize-diagnostics + CCACHE_BASEDIR"
+
+    mkdir -p dir1/src dir1/include
+    cat <<EOF >dir1/src/test.c
+#include <stdarg.h>
+#include <test.h>
+EOF
+    cat <<EOF >dir1/include/test.h
+int test;
+EOF
+    cp -r dir1 dir2
+    backdate dir1/include/test.h dir2/include/test.h
+
+    cat <<EOF >stderr.h
+int stderr(void)
+{
+  // Trigger warning by having no return statement.
+}
+EOF
+
+    unset CCACHE_NODIRECT
+
+    cd dir1
+    CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -w -MD -MF `pwd`/test.d -I`pwd`/include --serialize-diagnostics `pwd`/test.dia -c src/test.c -o `pwd`/test.o
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 4
+
+    cd ../dir2
+    CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -w -MD -MF `pwd`/test.d -I`pwd`/include --serialize-diagnostics `pwd`/test.dia -c src/test.c -o `pwd`/test.o
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 4
+}
+
+# =============================================================================
+
+SUITE_debug_prefix_map_PROBE() {
+    if ! $COMPILER_TYPE_GCC || $COMPILER_USES_MINGW; then
+        echo "-fdebug-prefix-map not supported by compiler"
+    fi
+}
+
+SUITE_debug_prefix_map_SETUP() {
+    unset CCACHE_NODIRECT
+
+    mkdir -p dir1/src dir1/include
+    cat <<EOF >dir1/src/test.c
+#include <stdarg.h>
+#include <test.h>
+EOF
+    cat <<EOF >dir1/include/test.h
+int test;
+EOF
+    cp -r dir1 dir2
+    backdate dir1/include/test.h dir2/include/test.h
+}
+
+SUITE_debug_prefix_map() {
+    # -------------------------------------------------------------------------
+    TEST "Mapping of debug info CWD"
+
+    cd dir1
+    CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`/include -g -fdebug-prefix-map=`pwd`=dir -c `pwd`/src/test.c -o `pwd`/test.o
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 2
+    if grep -E "[^=]`pwd`[^=]" test.o >/dev/null 2>&1; then
+        test_failed "Source dir (`pwd`) found in test.o"
+    fi
+
+    cd ../dir2
+    CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`/include -g -fdebug-prefix-map=`pwd`=dir -c `pwd`/src/test.c -o `pwd`/test.o
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 2
+    if grep -E "[^=]`pwd`[^=]" test.o >/dev/null 2>&1; then
+        test_failed "Source dir (`pwd`) found in test.o"
+    fi
+}
+
+# =============================================================================
+
+SUITE_masquerading_PROBE() {
+    local compiler_binary=$(echo $COMPILER | cut -d' ' -f1)
+    if [ "$(dirname $compiler_binary)" != . ]; then
+        echo "compiler ($compiler_binary) not taken from PATH"
+    fi
+}
+
+SUITE_masquerading_SETUP() {
+    local compiler_binary=$(echo $COMPILER | cut -d' ' -f1)
+    local compiler_args=$(echo $COMPILER | cut -s -d' ' -f2-)
+
+    ln -s "$CCACHE" $compiler_binary
+    CCACHE_COMPILE="./$compiler_binary $compiler_args"
+    generate_code 1 test1.c
+}
+
+SUITE_masquerading() {
+    # -------------------------------------------------------------------------
+    TEST "Masquerading via symlink"
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+    expect_equal_object_files reference_test1.o test1.o
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+    expect_equal_object_files reference_test1.o test1.o
+}
+
+# =============================================================================
+
+SUITE_hardlink_PROBE() {
+    touch file1
+    if ! ln file1 file2 >/dev/null 2>&1; then
+        echo "file system doesn't support hardlinks"
+    fi
 }
 
-nlevels1_suite() {
-    CCACHE_COMPILE="$CCACHE $COMPILER"
-    CCACHE_NLEVELS=1
-    export CCACHE_NLEVELS
-    base_tests
-    unset CCACHE_NLEVELS
+SUITE_hardlink() {
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_HARDLINK"
+
+    generate_code 1 test1.c
+
+    $UNCACHED_COMPILE -c -o reference_test1.o test1.c
+
+    $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+    expect_equal_object_files reference_test1.o test1.o
+
+    CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 1
+    expect_equal_object_files reference_test1.o test1.o
+
+    local obj_in_cache=$(find $CCACHE_DIR -name '*.o')
+    if [ ! $obj_in_cache -ef test1.o ]; then
+        test_failed "Object file not hard-linked to cached object file"
+    fi
 }
 
-direct_suite() {
+# =============================================================================
+
+SUITE_direct_SETUP() {
     unset CCACHE_NODIRECT
 
-    ##################################################################
-    # Create some code to compile.
     cat <<EOF >test.c
-/* test.c */
+// test.c
 #include "test1.h"
 #include "test2.h"
 EOF
@@ -762,455 +1362,450 @@ EOF
     cat <<EOF >test3.h
 int test3;
 EOF
-    cat <<EOF >code.c
-/* code.c */
-int test() {}
-EOF
     backdate test1.h test2.h test3.h
 
-    $COMPILER -c -Wp,-MD,expected.d test.c
-    expected_d_content=`cat expected.d`
+    $UNCACHED_COMPILE -c -Wp,-MD,expected.d test.c
+    $UNCACHED_COMPILE -c -Wp,-MMD,expected_mmd.d test.c
+    rm test.o
+}
+
+SUITE_direct() {
+    # -------------------------------------------------------------------------
+    TEST "Base case"
 
-    $COMPILER -c -Wp,-MMD,expected_mmd.d test.c
-    expected_mmd_d_content=`cat expected_mmd.d`
+    $UNCACHED_COMPILE -c -o reference_test.o test.c
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 2 # .o + .manifest
+    expect_equal_object_files reference_test.o test.o
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_stat 'files in cache' 2
+    expect_equal_object_files reference_test.o test.o
+
+    # -------------------------------------------------------------------------
+    TEST "Corrupt manifest file"
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    ##################################################################
-    # First compilation is a miss.
-    testname="first compilation"
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # Another compilation should now generate a direct hit.
-    testname="direct hit"
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-
-    ##################################################################
-    # Check that corrupt manifest files are handled and rewritten.
-    testname="corrupt manifest file"
-    $CCACHE -z >/dev/null
     manifest_file=`find $CCACHE_DIR -name '*.manifest'`
     rm $manifest_file
     touch $manifest_file
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 0
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 0
-
-    ##################################################################
-    # Compiling with CCACHE_NODIRECT set should generate a preprocessed hit.
-    testname="preprocessed hit"
-    $CCACHE -z >/dev/null
-    CCACHE_NODIRECT=1 $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 0
-
-    ##################################################################
-    # Test compilation of a modified include file.
-    testname="modified include file"
-    $CCACHE -z >/dev/null
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_NODIRECT"
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_NODIRECT=1 $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Modified include file"
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     echo "int test3_2;" >>test3.h
     backdate test3.h
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # A removed but previously compiled header file should be handled
-    # gracefully.
-    testname="missing header file"
-    $CCACHE -z >/dev/null
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
 
-    mv test1.h test1.h.saved
-    mv test3.h test3.h.saved
+    # -------------------------------------------------------------------------
+    TEST "Removed but previously compiled header file"
+
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    rm test3.h
     cat <<EOF >test1.h
-/* No more include of test3.h */
+// No more include of test3.h
 int test1;
 EOF
     backdate test1.h
 
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
 
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
 
-    # Restore
-    mv test1.h.saved test1.h
-    mv test3.h.saved test3.h
-
-    rm -f other.d
+    # -------------------------------------------------------------------------
+    TEST "Calculation of dependency file names"
 
-    ##################################################################
-    # Check calculation of dependency file names.
-    $CCACHE -Cz >/dev/null
-    checkstat 'files in cache' 0
     mkdir test.dir
     for ext in .obj "" . .foo.bar; do
-        testname="dependency file calculation from object file 'test$ext'"
         dep_file=test.dir/`echo test$ext | sed 's/\.[^.]*\$//'`.d
-        $CCACHE $COMPILER -MD -c test.c -o test.dir/test$ext
+        $CCACHE_COMPILE -MD -c test.c -o test.dir/test$ext
         rm -f $dep_file
-        $CCACHE $COMPILER -MD -c test.c -o test.dir/test$ext
+        $CCACHE_COMPILE -MD -c test.c -o test.dir/test$ext
         if [ ! -f $dep_file ]; then
             test_failed "$dep_file missing"
         fi
     done
-    rm -rf test.dir
-    checkstat 'files in cache' 12
+    expect_stat 'files in cache' 12
 
-    ##################################################################
-    # Check that -Wp,-MD,file.d works.
-    testname="-Wp,-MD"
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c -Wp,-MD,other.d test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_d_content"
-    CCACHE_DISABLE=1 $COMPILER -c -Wp,-MD,other.d test.c -o reference_test.o
-    compare_file reference_test.o test.o
+    # -------------------------------------------------------------------------
+    TEST "-Wp,-MD"
 
-    rm -f other.d
+    $CCACHE_COMPILE -c -Wp,-MD,other.d test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files other.d expected.d
 
-    $CCACHE $COMPILER -c -Wp,-MD,other.d test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    $UNCACHED_COMPILE -c -Wp,-MD,other.d test.c -o reference_test.o
+    expect_equal_object_files reference_test.o test.o
 
     rm -f other.d
+    $CCACHE_COMPILE -c -Wp,-MD,other.d test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files other.d expected.d
+    expect_equal_object_files reference_test.o test.o
+
+    $CCACHE_COMPILE -c -Wp,-MD,different_name.d test.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files different_name.d expected.d
+    expect_equal_object_files reference_test.o test.o
+
+    # -------------------------------------------------------------------------
+    TEST "-Wp,-MMD"
+
+    $CCACHE_COMPILE -c -Wp,-MMD,other.d test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files other.d expected_mmd.d
+
+    $UNCACHED_COMPILE -c -Wp,-MMD,other.d test.c -o reference_test.o
+    expect_equal_object_files reference_test.o test.o
 
-    $CCACHE $COMPILER -c -Wp,-MD,different_name.d test.c
-    checkstat 'cache hit (direct)' 2
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile different_name.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    rm -f other.d
+    $CCACHE_COMPILE -c -Wp,-MMD,other.d test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files other.d expected_mmd.d
+    expect_equal_object_files reference_test.o test.o
+
+    $CCACHE_COMPILE -c -Wp,-MMD,different_name.d test.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files different_name.d expected_mmd.d
+    expect_equal_object_files reference_test.o test.o
+
+    # -------------------------------------------------------------------------
+    TEST "-Wp,-D"
+
+    $CCACHE_COMPILE -c -Wp,-DFOO test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c -DFOO test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "-Wp, with multiple arguments"
+
+    # ccache could try to parse and make sense of -Wp, with multiple arguments,
+    # but it currently doesn't, so we have to disable direct mode.
+
+    $CCACHE_COMPILE -c -Wp,-DFOO,-DGOO test.c 2>/dev/null
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c -Wp,-DFOO,-DGOO test.c 2>/dev/null
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Multiple object entries in manifest"
 
-    rm -f different_name.d
+    for i in 0 1 2 3 4; do
+        echo "int test1_$i;" >>test1.h
+        backdate test1.h
+        $CCACHE_COMPILE -c test.c
+        $CCACHE_COMPILE -c test.c
+    done
+    expect_stat 'cache hit (direct)' 5
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 5
 
-    ##################################################################
-    # Check that -Wp,-MMD,file.d works.
-    testname="-Wp,-MMD"
-    $CCACHE -C >/dev/null
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c -Wp,-MMD,other.d test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_mmd_d_content"
-    CCACHE_DISABLE=1 $COMPILER -c -Wp,-MMD,other.d test.c -o reference_test.o
-    compare_file reference_test.o test.o
+    # -------------------------------------------------------------------------
+    TEST "-MD"
 
-    rm -f other.d
+    $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
 
-    $CCACHE $COMPILER -c -Wp,-MMD,other.d test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_mmd_d_content"
-    compare_file reference_test.o test.o
+    $UNCACHED_COMPILE -c -MD test.c -o reference_test.o
+    expect_equal_object_files reference_test.o test.o
 
-    rm -f other.d
+    rm -f test.d
+    $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
+    expect_equal_object_files reference_test.o test.o
 
-    $CCACHE $COMPILER -c -Wp,-MMD,different_name.d test.c
-    checkstat 'cache hit (direct)' 2
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile different_name.d "$expected_mmd_d_content"
-    compare_file reference_test.o test.o
+    # -------------------------------------------------------------------------
+    TEST "-ftest-coverage"
 
-    rm -f different_name.d
+    cat <<EOF >code.c
+int test() { return 0; }
+EOF
 
-    ##################################################################
-    # Test some header modifications to get multiple objects in the manifest.
-    testname="several objects"
-    $CCACHE -z >/dev/null
-    for i in 0 1 2 3 4; do
-        echo "int test1_$i;" >>test1.h
-        backdate test1.h
-        $CCACHE $COMPILER -c test.c
-        $CCACHE $COMPILER -c test.c
-    done
-    checkstat 'cache hit (direct)' 5
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 5
+    $CCACHE_COMPILE -c -fprofile-arcs -ftest-coverage code.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    test -r code.gcno || test_failed "code.gcno missing"
 
-    ##################################################################
-    # Check that -MD works.
-    testname="-MD"
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile test.d "$expected_d_content"
-    CCACHE_DISABLE=1 $COMPILER -c -MD test.c -o reference_test.o
-    compare_file reference_test.o test.o
+    rm code.gcno
 
-    rm -f test.d
+    $CCACHE_COMPILE -c -fprofile-arcs -ftest-coverage code.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    test -r code.gcno || test_failed "code.gcno missing"
 
-    $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile test.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    # -------------------------------------------------------------------------
+    TEST "Direct mode on cache created by ccache without direct mode support"
 
-    ##################################################################
-    # Check that coverage works.
-    testname="coverage (empty)"
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c -fprofile-arcs -ftest-coverage test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c -fprofile-arcs -ftest-coverage test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    testname="coverage (code)"
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c -fprofile-arcs -ftest-coverage code.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    test -r code.gcno || test_failed "gcov"
-
-    rm -f code.gcno
-
-    $CCACHE $COMPILER -c -fprofile-arcs -ftest-coverage code.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    test -r code.gcno || test_failed "gcov"
-
-    ##################################################################
-    # Check the scenario of running a ccache with direct mode on a cache
-    # built up by a ccache without direct mode support.
-    testname="direct mode on old cache"
-    $CCACHE -z >/dev/null
-    $CCACHE -C >/dev/null
-    CCACHE_NODIRECT=1 $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile test.d "$expected_d_content"
-    CCACHE_DISABLE=1 $COMPILER -c -MD test.c -o reference_test.o
-    compare_file reference_test.o test.o
+    CCACHE_NODIRECT=1 $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
+    $UNCACHED_COMPILE -c -MD test.c -o reference_test.o
+    expect_equal_object_files reference_test.o test.o
 
     rm -f test.d
 
-    CCACHE_NODIRECT=1 $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-    checkfile test.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    CCACHE_NODIRECT=1 $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
+    expect_equal_object_files reference_test.o test.o
 
     rm -f test.d
 
-    $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 1
-    checkfile test.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
+    expect_equal_object_files reference_test.o test.o
 
     rm -f test.d
 
-    $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 1
-    checkfile test.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
+    expect_equal_object_files reference_test.o test.o
 
-    ##################################################################
-    # Check that -MF works.
-    testname="-MF"
-    $CCACHE -C >/dev/null
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -c -MD -MF other.d test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_d_content"
-    CCACHE_DISABLE=1 $COMPILER -c -MD -MF other.d test.c -o reference_test.o
-    compare_file reference_test.o test.o
+    # -------------------------------------------------------------------------
+    TEST "-MF"
+
+    $CCACHE_COMPILE -c -MD -MF other.d test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files other.d expected.d
+    $UNCACHED_COMPILE -c -MD -MF other.d test.c -o reference_test.o
+    expect_equal_object_files reference_test.o test.o
 
     rm -f other.d
 
-    $CCACHE $COMPILER -c -MD -MF other.d test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    $CCACHE_COMPILE -c -MD -MF other.d test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files other.d expected.d
+    expect_equal_object_files reference_test.o test.o
 
-    $CCACHE $COMPILER -c -MD -MF different_name.d test.c
-    checkstat 'cache hit (direct)' 2
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile different_name.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    $CCACHE_COMPILE -c -MD -MF different_name.d test.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files different_name.d expected.d
+    expect_equal_object_files reference_test.o test.o
 
     rm -f different_name.d
 
-    $CCACHE $COMPILER -c -MD -MFthird_name.d test.c
-    checkstat 'cache hit (direct)' 3
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile third_name.d "$expected_d_content"
-    compare_file reference_test.o test.o
+    $CCACHE_COMPILE -c -MD -MFthird_name.d test.c
+    expect_stat 'cache hit (direct)' 3
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files third_name.d expected.d
+    expect_equal_object_files reference_test.o test.o
 
     rm -f third_name.d
 
-    ##################################################################
-    # Check that a missing .d file in the cache is handled correctly.
-    testname="missing dependency file"
-    $CCACHE -z >/dev/null
-    $CCACHE -C >/dev/null
+    # -------------------------------------------------------------------------
+    TEST "Missing .d file"
 
-    $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_d_content"
-    CCACHE_DISABLE=1 $COMPILER -c -MD test.c -o reference_test.o
-    compare_file reference_test.o test.o
-
-    $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_d_content"
-    compare_file reference_test.o test.o
-
-    find $CCACHE_DIR -name '*.d' -exec rm -f '{}' \;
-
-    $CCACHE $COMPILER -c -MD test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-    checkfile other.d "$expected_d_content"
-    compare_file reference_test.o test.o
-
-    ##################################################################
-    # Check that stderr from both the preprocessor and the compiler is emitted
-    # in direct mode too.
-    testname="cpp stderr"
-    $CCACHE -z >/dev/null
-    $CCACHE -C >/dev/null
-cat <<EOF >cpp-warning.c
+    $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
+
+    find $CCACHE_DIR -name '*.d' -delete
+
+    $CCACHE_COMPILE -c -MD test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_equal_files test.d expected.d
+
+    # -------------------------------------------------------------------------
+    TEST "stderr from both preprocessor and compiler"
+
+    cat <<EOF >cpp-warning.c
 #if FOO
-/* Trigger preprocessor warning about extra token after #endif. */
+// Trigger preprocessor warning about extra token after #endif.
 #endif FOO
 int stderr(void)
 {
-       /* Trigger compiler warning by having no return statement. */
+  // Trigger compiler warning by having no return statement.
 }
 EOF
-    $CCACHE $COMPILER -Wall -W -c cpp-warning.c 2>stderr-orig.txt
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    $CCACHE_COMPILE -Wall -W -c cpp-warning.c 2>stderr-orig.txt
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_NODIRECT=1 $CCACHE_COMPILE -Wall -W -c cpp-warning.c 2>stderr-cpp.txt
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_file_content stderr-cpp.txt "`cat stderr-orig.txt`"
+
+    $CCACHE_COMPILE -Wall -W -c cpp-warning.c 2>stderr-mf.txt
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+    expect_file_content stderr-mf.txt "`cat stderr-orig.txt`"
+
+    # -------------------------------------------------------------------------
+    TEST "Empty source file"
 
-    CCACHE_NODIRECT=1
-    export CCACHE_NODIRECT
-    $CCACHE $COMPILER -Wall -W -c cpp-warning.c 2>stderr-cpp.txt
-    unset CCACHE_NODIRECT
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-    checkfile stderr-cpp.txt "`cat stderr-orig.txt`"
-
-    $CCACHE $COMPILER -Wall -W -c cpp-warning.c 2>stderr-mf.txt
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-    checkfile stderr-mf.txt "`cat stderr-orig.txt`"
-
-    ##################################################################
-    # Check that it is possible to compile and cache an empty source code file.
-    testname="empty source file"
-    $CCACHE -Cz >/dev/null
-    cp /dev/null empty.c
-    $CCACHE $COMPILER -c empty.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c empty.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # Check that empty include files are handled as well.
-    testname="empty include file"
-    $CCACHE -Cz >/dev/null
-    cp /dev/null empty.h
+    touch empty.c
+
+    $CCACHE_COMPILE -c empty.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c empty.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Empty include file"
+
+    touch empty.h
     cat <<EOF >include_empty.c
 #include "empty.h"
 EOF
     backdate empty.h
-    $CCACHE $COMPILER -c include_empty.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c include_empty.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # Check that direct mode correctly detects file name/path changes.
-    testname="__FILE__ in source file"
-    $CCACHE -Cz >/dev/null
+    $CCACHE_COMPILE -c include_empty.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    $CCACHE_COMPILE -c include_empty.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__FILE__ in source file disables direct mode"
+
     cat <<EOF >file.c
 #define file __FILE__
 int test;
 EOF
-    $CCACHE $COMPILER -c file.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c file.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c `pwd`/file.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
-
-    testname="__FILE__ in include file"
-    $CCACHE -Cz >/dev/null
+
+    $CCACHE_COMPILE -c file.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c file.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c `pwd`/file.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__FILE__ in include file disables direct mode"
+
     cat <<EOF >file.h
 #define file __FILE__
 int test;
@@ -1219,43 +1814,50 @@ EOF
     cat <<EOF >file_h.c
 #include "file.h"
 EOF
-    $CCACHE $COMPILER -c file_h.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c file_h.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+
+    $CCACHE_COMPILE -c file_h.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c file_h.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     mv file_h.c file2_h.c
-    $CCACHE $COMPILER -c `pwd`/file2_h.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
-
-    ##################################################################
-    # Check that direct mode ignores __FILE__ if sloppiness is specified.
-    testname="__FILE__ in source file, sloppy"
-    $CCACHE -Cz >/dev/null
+
+    $CCACHE_COMPILE -c `pwd`/file2_h.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__FILE__ in source file ignored if sloppy"
+
     cat <<EOF >file.c
 #define file __FILE__
 int test;
 EOF
-    CCACHE_SLOPPINESS="$default_sloppiness file_macro" $CCACHE $COMPILER -c file.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness file_macro" $CCACHE $COMPILER -c file.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness file_macro" $CCACHE $COMPILER -c `pwd`/file.c
-    checkstat 'cache hit (direct)' 2
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    testname="__FILE__ in include file, sloppy"
-    $CCACHE -Cz >/dev/null
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS file_macro" $CCACHE_COMPILE -c file.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS file_macro" $CCACHE_COMPILE -c file.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS file_macro" $CCACHE_COMPILE -c `pwd`/file.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__FILE__ in include file ignored if sloppy"
+
     cat <<EOF >file.h
 #define file __FILE__
 int test;
@@ -1264,75 +1866,86 @@ EOF
     cat <<EOF >file_h.c
 #include "file.h"
 EOF
-    CCACHE_SLOPPINESS="$default_sloppiness file_macro" $CCACHE $COMPILER -c file_h.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness file_macro" $CCACHE $COMPILER -c file_h.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS file_macro" $CCACHE_COMPILE -c file_h.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS file_macro" $CCACHE_COMPILE -c file_h.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     mv file_h.c file2_h.c
-    CCACHE_SLOPPINESS="$default_sloppiness file_macro" $CCACHE $COMPILER -c `pwd`/file2_h.c
-    checkstat 'cache hit (direct)' 2
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # Check that we never get direct hits when __TIME__ is used.
-    testname="__TIME__ in source file"
-    $CCACHE -Cz >/dev/null
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS file_macro" $CCACHE_COMPILE -c `pwd`/file2_h.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__TIME__ in source file disables direct mode"
+
     cat <<EOF >time.c
 #define time __TIME__
 int test;
 EOF
-    $CCACHE $COMPILER -c time.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c time.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
 
-    testname="__TIME__ in include time"
-    $CCACHE -Cz >/dev/null
+    $CCACHE_COMPILE -c time.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c time.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__TIME__ in include file disables direct mode"
+
     cat <<EOF >time.h
 #define time __TIME__
 int test;
 EOF
     backdate time.h
+
     cat <<EOF >time_h.c
 #include "time.h"
 EOF
-    $CCACHE $COMPILER -c time_h.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c time_h.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # Check that direct mode ignores __TIME__ when sloppiness is specified.
-    testname="__TIME__ in source file, sloppy"
-    $CCACHE -Cz >/dev/null
+
+    $CCACHE_COMPILE -c time_h.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c time_h.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__TIME__ in source file ignored if sloppy"
+
     cat <<EOF >time.c
 #define time __TIME__
 int test;
 EOF
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
 
-    testname="__TIME__ in include time, sloppy"
-    $CCACHE -Cz >/dev/null
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE -c time.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE -c time.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "__TIME__ in include file ignored if sloppy"
+
     cat <<EOF >time.h
 #define time __TIME__
 int test;
@@ -1341,19 +1954,19 @@ EOF
     cat <<EOF >time_h.c
 #include "time.h"
 EOF
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time_h.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time_h.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # Check that a too new include file turns off direct mode.
-    testname="too new include file"
-    $CCACHE -Cz >/dev/null
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE -c time_h.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE -c time_h.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Too new include file disables direct mode"
+
     cat <<EOF >new.c
 #include "new.h"
 EOF
@@ -1361,19 +1974,20 @@ EOF
 int test;
 EOF
     touch -t 203801010000 new.h
-    $CCACHE $COMPILER -c new.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c new.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-
-    ##################################################################
-    # Check that include file mtime is ignored when sloppiness is specified.
-    testname="too new include file, sloppy"
-    $CCACHE -Cz >/dev/null
+
+    $CCACHE_COMPILE -c new.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c new.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "New include file ignored if sloppy"
+
     cat <<EOF >new.c
 #include "new.h"
 EOF
@@ -1381,20 +1995,22 @@ EOF
 int test;
 EOF
     touch -t 203801010000 new.h
-    CCACHE_SLOPPINESS="$default_sloppiness include_file_mtime" $CCACHE $COMPILER -c new.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness include_file_mtime" $CCACHE $COMPILER -c new.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    ##################################################################
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS include_file_mtime" $CCACHE_COMPILE -c new.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS include_file_mtime" $CCACHE_COMPILE -c new.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
     # Check that environment variables that affect the preprocessor are taken
     # into account.
-    testname="environment variables"
-    $CCACHE -Cz >/dev/null
+    TEST "CPATH included in hash"
+
     rm -rf subdir1 subdir2
     mkdir subdir1 subdir2
     cat <<EOF >subdir1/foo.h
@@ -1407,53 +2023,63 @@ EOF
 #include <foo.h>
 EOF
     backdate subdir1/foo.h subdir2/foo.h
-    CPATH=subdir1 $CCACHE $COMPILER -c foo.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CPATH=subdir1 $CCACHE $COMPILER -c foo.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CPATH=subdir2 $CCACHE $COMPILER -c foo.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2 # subdir2 is part of the preprocessor output
-    CPATH=subdir2 $CCACHE $COMPILER -c foo.c
-    checkstat 'cache hit (direct)' 2
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
-
-    testname="comment in strings"
-    $CCACHE -Cz >/dev/null
+
+    CPATH=subdir1 $CCACHE_COMPILE -c foo.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CPATH=subdir1 $CCACHE_COMPILE -c foo.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CPATH=subdir2 $CCACHE_COMPILE -c foo.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    CPATH=subdir2 $CCACHE_COMPILE -c foo.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Comment in strings"
+
     echo 'char *comment = " /* \\\\u" "foo" " */";' >comment.c
-    $CCACHE $COMPILER -c comment.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -c comment.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+
+    $CCACHE_COMPILE -c comment.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -c comment.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     echo 'char *comment = " /* \\\\u" "goo" " */";' >comment.c
-    $CCACHE $COMPILER -c comment.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
-
-    #################################################################
-    # Check that strange "#line" directives are handled.
-    testname="#line directives with troublesome files"
-    $CCACHE -Cz >/dev/null
+
+    $CCACHE_COMPILE -c comment.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "#line directives with troublesome files"
+
     cat <<EOF >strange.c
 int foo;
 EOF
     for x in stdout tty sda hda; do
         if [ -b /dev/$x ] || [ -c /dev/$x ]; then
-            echo "#line 1 \"/dev/$x\"" >> strange.c
+            echo "#line 1 \"/dev/$x\"" >>strange.c
         fi
     done
-    CCACHE_SLOPPINESS="$default_sloppiness include_file_mtime" $CCACHE $COMPILER -c strange.c
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS include_file_mtime" $CCACHE_COMPILE -c strange.c
+
     manifest=`find $CCACHE_DIR -name '*.manifest'`
     if [ -n "$manifest" ]; then
         data="`$CCACHE --dump-manifest $manifest | egrep '/dev/(stdout|tty|sda|hda'`"
@@ -1462,25 +2088,25 @@ EOF
         fi
     fi
 
-    ##################################################################
-    # Test --dump-manifest output.
-    testname="--dump-manifest"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER test.c -c -o test.o
+    # -------------------------------------------------------------------------
+    TEST "--dump-manifest"
+
+    $CCACHE_COMPILE test.c -c -o test.o
+
     manifest=`find $CCACHE_DIR -name '*.manifest'`
     $CCACHE --dump-manifest $manifest >manifest.dump
 
-    if grep 'Hash: e6b009695d072974f2c4d1dd7e7ed4fc' manifest.dump >/dev/null 2>&1 && \
+    if grep 'Hash: d4de2f956b4a386c6660990a7a1ab13f' manifest.dump >/dev/null 2>&1 && \
        grep 'Hash: e94ceb9f1b196c387d098a5f1f4fe862' manifest.dump >/dev/null 2>&1 && \
-       grep 'Hash: c2f5392dbc7e8ff6138d01608445240a' manifest.dump >/dev/null 2>&1; then
+       grep 'Hash: ba753bebf9b5eb99524bb7447095e2e6' manifest.dump >/dev/null 2>&1; then
         : OK
     else
-        test_failed "unexpected output of --dump-manifest"
+        test_failed "Unexpected output of --dump-manifest"
     fi
 
-    ##################################################################
-    testname="argument-less -B and -L"
-    $CCACHE -Cz > /dev/null
+    # -------------------------------------------------------------------------
+    TEST "Argument-less -B and -L"
+
     cat <<EOF >test.c
 #include <stdio.h>
 int main(void)
@@ -1492,19 +2118,42 @@ int main(void)
 }
 EOF
 
-    $CCACHE $COMPILER -A -L -DFOO -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER -A -L -DBAR -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache miss' 2
+    $CCACHE_COMPILE -B -L -DFOO -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache miss' 1
+
+    $CCACHE_COMPILE -B -L -DBAR -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_IGNOREHEADERS"
+
+    cat <<EOF >ignore.h
+// We don't want this header in the manifest.
+EOF
+    backdate ignore.h
+    cat <<EOF >ignore.c
+#include "ignore.h"
+int foo;
+EOF
+
+    CCACHE_IGNOREHEADERS="ignore.h" $CCACHE_COMPILE -c ignore.c
+    manifest=`find $CCACHE_DIR -name '*.manifest'`
+    data="`$CCACHE --dump-manifest $manifest | grep ignore.h`"
+    if [ -n "$data" ]; then
+        test_failed "$manifest contained ignored header: $data"
+    fi
 }
 
-basedir_suite() {
-    ##################################################################
-    # Create some code to compile.
+# =============================================================================
+
+SUITE_basedir_SETUP() {
+    unset CCACHE_NODIRECT
+
     mkdir -p dir1/src dir1/include
     cat <<EOF >dir1/src/test.c
+#include <stdarg.h>
 #include <test.h>
 EOF
     cat <<EOF >dir1/include/test.h
@@ -1512,224 +2161,199 @@ int test;
 EOF
     cp -r dir1 dir2
     backdate dir1/include/test.h dir2/include/test.h
-
-    cat <<EOF >stderr.h
-int stderr(void)
-{
-       /* Trigger warning by having no return statement. */
 }
-EOF
-    cat <<EOF >stderr.c
-#include <stderr.h>
-EOF
-    backdate stderr.h
 
-    ##################################################################
-    # CCACHE_BASEDIR="" and using absolute include path will result in a cache
-    # miss.
-    testname="empty CCACHE_BASEDIR"
-    $CCACHE -z >/dev/null
+SUITE_basedir() {
+    # -------------------------------------------------------------------------
+    TEST "Enabled CCACHE_BASEDIR"
 
     cd dir1
-    CCACHE_BASEDIR="" $CCACHE $COMPILER -I`pwd`/include -c src/test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    cd ..
-
-    cd dir2
-    CCACHE_BASEDIR="" $CCACHE $COMPILER -I`pwd`/include -c src/test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
-    cd ..
-
-    ##################################################################
-    # Setting CCACHE_BASEDIR will result in a cache hit because include paths
-    # in the preprocessed output are rewritten.
-    testname="set CCACHE_BASEDIR"
-    $CCACHE -z >/dev/null
-    $CCACHE -C >/dev/null
+    CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I`pwd`/include -c src/test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    ln -s dir1 symlink_to_dir1
-    cd symlink_to_dir1
-    CCACHE_BASEDIR="`pwd`" $CCACHE $COMPILER -I`pwd`/include -c src/test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    cd ..
-
-    cd dir2
-    # The space after -I is there to test an extra code path.
-    CCACHE_BASEDIR="`pwd`" $CCACHE $COMPILER -I `pwd`/include -c src/test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-    cd ..
-
-    ##################################################################
-    # Setting CCACHE_BASEDIR will result in a cache hit because -I arguments
-    # are rewritten, as are the paths stored in the manifest.
-    testname="set CCACHE_BASEDIR, direct lookup"
-    $CCACHE -z >/dev/null
-    $CCACHE -C >/dev/null
-    unset CCACHE_NODIRECT
+    cd ../dir2
+    CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I`pwd`/include -c src/test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Disabled (default) CCACHE_BASEDIR"
 
     cd dir1
-    CCACHE_BASEDIR="`pwd`" $CCACHE $COMPILER -I`pwd`/include -c src/test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    cd ..
-
-    cd dir2
-    CCACHE_BASEDIR="`pwd`" $CCACHE $COMPILER -I`pwd`/include -c src/test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    cd ..
-
-    ##################################################################
-    # CCACHE_BASEDIR="" is the default.
-    testname="default CCACHE_BASEDIR"
+    CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I`pwd`/include -c src/test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # CCACHE_BASEDIR="" is the default:
+    $CCACHE_COMPILE -I`pwd`/include -c src/test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Path normalization"
+
     cd dir1
-    $CCACHE -z >/dev/null
-    $CCACHE $COMPILER -I`pwd`/include -c src/test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    cd ..
+    CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I`pwd`/include -c src/test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    ##################################################################
     # Rewriting triggered by CCACHE_BASEDIR should handle paths with multiple
-    # slashes correctly.
-    testname="path normalization"
-    cd dir1
-    $CCACHE -z >/dev/null
-    CCACHE_BASEDIR=`pwd` $CCACHE $COMPILER -I`pwd`//include -c `pwd`//src/test.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    cd ..
-
-    ##################################################################
-    # Check that rewriting triggered by CCACHE_BASEDIR also affects stderr.
-    testname="stderr"
-    $CCACHE -z >/dev/null
-    CCACHE_BASEDIR=`pwd` $CCACHE $COMPILER -Wall -W -I`pwd` -c `pwd`/stderr.c -o `pwd`/stderr.o 2>stderr.txt
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    # slashes correctly:
+    CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`//include -c `pwd`//src/test.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Rewriting in stderr"
+
+    cat <<EOF >stderr.h
+int stderr(void)
+{
+  // Trigger warning by having no return statement.
+}
+EOF
+    backdate stderr.h
+    cat <<EOF >stderr.c
+#include <stderr.h>
+EOF
+
+    CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -Wall -W -I`pwd` -c `pwd`/stderr.c -o `pwd`/stderr.o 2>stderr.txt
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     if grep `pwd` stderr.txt >/dev/null 2>&1; then
         test_failed "Base dir (`pwd`) found in stderr:\n`cat stderr.txt`"
     fi
 
-    CCACHE_BASEDIR=`pwd` $CCACHE $COMPILER -Wall -W -I`pwd` -c `pwd`/stderr.c -o `pwd`/stderr.o 2>stderr.txt
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -Wall -W -I`pwd` -c `pwd`/stderr.c -o `pwd`/stderr.o 2>stderr.txt
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     if grep `pwd` stderr.txt >/dev/null 2>&1; then
         test_failed "Base dir (`pwd`) found in stderr:\n`cat stderr.txt`"
     fi
 
-    ##################################################################
-    # Check that -MF, -MQ and -MT arguments with absolute paths are rewritten
-    # to relative.
-    testname="-MF/-MQ/-MT with absolute paths"
+    # -------------------------------------------------------------------------
+    TEST "-MF/-MQ/-MT with absolute paths"
+
     for option in MF "MF " MQ "MQ " MT "MT "; do
-        $CCACHE -Cz >/dev/null
+        clear_cache
         cd dir1
-        CCACHE_BASEDIR="`pwd`" $CCACHE $COMPILER -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
-        checkstat 'cache hit (direct)' 0
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 1
+        CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
+        expect_stat 'cache hit (direct)' 0
+        expect_stat 'cache hit (preprocessed)' 0
+        expect_stat 'cache miss' 1
         cd ..
 
         cd dir2
-        CCACHE_BASEDIR="`pwd`" $CCACHE $COMPILER -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
-        checkstat 'cache hit (direct)' 1
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 1
+        CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
+        expect_stat 'cache hit (direct)' 1
+        expect_stat 'cache hit (preprocessed)' 0
+        expect_stat 'cache miss' 1
         cd ..
     done
 
-    ##################################################################
-    # Check that clang's --serialize-diagnostics arguments with absolute paths
-    # are rewritten to relative.
-    if [ $COMPILER_TYPE_CLANG -eq 1 ]; then
-        testname="serialize-diagnostics"
-        $CCACHE -Cz >/dev/null
+    # -------------------------------------------------------------------------
+    # When BASEDIR is set to /, check that -MF, -MQ and -MT arguments with
+    # absolute paths are rewritten to relative and that the dependency file
+    # only contains relative paths.
+    TEST "-MF/-MQ/-MT with absolute paths and BASEDIR set to /"
+
+    for option in MF "MF " MQ "MQ " MT "MT "; do
+        clear_cache
         cd dir1
-        CCACHE_BASEDIR=`pwd` $CCACHE $COMPILER -w -MD -MF `pwd`/test.d -I`pwd`/include --serialize-diagnostics `pwd`/test.dia -c src/test.c -o `pwd`/test.o
-        checkstat 'cache hit (direct)' 0
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 1
-        checkstat 'files in cache' 4
+        CCACHE_BASEDIR="/" $CCACHE_COMPILE -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
+        expect_stat 'cache hit (direct)' 0
+        expect_stat 'cache hit (preprocessed)' 0
+        expect_stat 'cache miss' 1
+        # Check that there is no absolute path in the dependency file:
+        while read line; do
+            for file in $line; do
+                case $file in /*)
+                    test_failed "Absolute file path '$file' found in dependency file '`pwd`/test.d'"
+                esac
+            done
+        done <test.d
         cd ..
 
         cd dir2
-        CCACHE_BASEDIR=`pwd` $CCACHE $COMPILER -w -MD -MF `pwd`/test.d -I`pwd`/include --serialize-diagnostics `pwd`/test.dia -c src/test.c -o `pwd`/test.o
-        checkstat 'cache hit (direct)' 1
-        checkstat 'cache hit (preprocessed)' 0
-        checkstat 'cache miss' 1
-        checkstat 'files in cache' 4
+        CCACHE_BASEDIR="/" $CCACHE_COMPILE -I`pwd`/include -MD -${option}`pwd`/test.d -c src/test.c
+        expect_stat 'cache hit (direct)' 1
+        expect_stat 'cache hit (preprocessed)' 0
+        expect_stat 'cache miss' 1
         cd ..
-    fi
+    done
 }
 
-compression_suite() {
-    ##################################################################
-    # Create some code to compile.
-    cat <<EOF >test.c
-int test;
-EOF
+# =============================================================================
+
+SUITE_compression_SETUP() {
+    generate_code 1 test.c
+}
+
+SUITE_compression() {
+    # -------------------------------------------------------------------------
+    TEST "Hash sum equal for compressed and uncompressed files"
+
+    CCACHE_COMPRESS=1 $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_COMPRESS=1 $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
 
-    ##################################################################
-    # Check that compressed and uncompressed files get the same hash sum.
-    testname="compression hash sum"
-    CCACHE_COMPRESS=1 $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 1
+}
 
-    CCACHE_COMPRESS=1 $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+# =============================================================================
 
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 1
+SUITE_readonly_SETUP() {
+    generate_code 1 test.c
+    generate_code 2 test2.c
 }
 
-readonly_suite() {
-    ##################################################################
-    # Create some code to compile.
-    echo "int test;" >test.c
-    echo "int test2;" >test2.c
+SUITE_readonly() {
+    # -------------------------------------------------------------------------
+    TEST "Cache hit"
 
     # Cache a compilation.
-    $CCACHE $COMPILER -c test.c -o test.o
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    # Make the cache readonly
-    # Check that readonly mode finds the result.
-    testname="cache hit"
-    rm -f test.o test2.o
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    rm test.o
+
+    # Make the cache read-only.
     chmod -R a-w $CCACHE_DIR
-    CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp CCACHE_PREFIX=false $CCACHE $COMPILER -c test.c -o test.o >/dev/null 2>&1
+
+    # Check that read-only mode finds the cached result.
+    CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp CCACHE_PREFIX=false $CCACHE_COMPILE -c test.c
     status1=$?
+
     # Check that fallback to the real compiler works for a cache miss.
-    CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp $CCACHE $COMPILER -c test2.c -o test2.o >/dev/null 2>&1
+    CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp $CCACHE_COMPILE -c test2.c
     status2=$?
-    chmod -R a+w $CCACHE_DIR
+
+    # Leave test dir a nice state after test failure.
+    chmod -R +w $CCACHE_DIR
+
     if [ $status1 -ne 0 ]; then
-        test_failed "failure when compiling test.c readonly"
+        test_failed "Failure when compiling test.c read-only"
     fi
     if [ $status2 -ne 0 ]; then
-        test_failed "failure when compiling test2.c readonly"
+        test_failed "Failure when compiling test2.c read-only"
     fi
     if [ ! -f test.o ]; then
         test_failed "test.o missing"
@@ -1738,165 +2362,142 @@ readonly_suite() {
         test_failed "test2.o missing"
     fi
 
-    # Check that readonly mode doesn't try to store new results.
-    testname="cache miss"
-    files_before=`find $CCACHE_DIR -type f | wc -l`
-    CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp $CCACHE $COMPILER -c test2.c -o test2.o
-    if [ $? -ne 0 ]; then
-        test_failed "failure when compiling test2.c readonly"
-    fi
-    if [ ! -f test2.o ]; then
-        test_failed "test2.o missing"
-    fi
-    files_after=`find $CCACHE_DIR -type f | wc -l`
-    if [ $files_before -ne $files_after ]; then
-        test_failed "readonly mode stored files in the cache"
-    fi
+    # -------------------------------------------------------------------------
+    TEST "Cache miss"
 
-    # Check that readonly mode and direct mode works.
-    unset CCACHE_NODIRECT
-    files_before=`find $CCACHE_DIR -type f | wc -l`
-    CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp $CCACHE $COMPILER -c test.c -o test.o
-    CCACHE_NODIRECT=1
-    export CCACHE_NODIRECT
+    # Check that read-only mode doesn't try to store new results.
+    CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp $CCACHE_COMPILE -c test.c
     if [ $? -ne 0 ]; then
-        test_failed "failure when compiling test2.c readonly"
+        test_failed "Failure when compiling test2.c read-only"
     fi
-    files_after=`find $CCACHE_DIR -type f | wc -l`
-    if [ $files_before -ne $files_after ]; then
-        test_failed "readonly mode + direct mode stored files in the cache"
+    if [ -d $CCACHE_DIR ]; then
+        test_failed "ccache dir was created"
     fi
 
-    ##################################################################
-}
+    # -------------------------------------------------------------------------
+    # Check that read-only mode and direct mode work together.
+    TEST "Cache hit, direct"
 
-readonly_direct_suite() {
-    unset CCACHE_NODIRECT
+    # Cache a compilation.
+    $CCACHE_COMPILE -c test.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    rm test.o
 
-    ##################################################################
-    # Create some code to compile.
-    echo "int test;" >test.c
+    # Make the cache read-only.
+    chmod -R a-w $CCACHE_DIR
 
-    # Cache a compilation.
-    testname="fill cache"
-    $CCACHE $COMPILER -c test.c -o test.o
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    # Check that "readonly direct" mode gets a direct hit.
-    testname="direct hit"
-    CCACHE_READONLY_DIRECT=1 $CCACHE $COMPILER -c test.c -o test.o
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    # Check that "readonly direct" mode doesn't get a preprocessed hit.
-    testname="preprocessed miss"
-    CCACHE_READONLY_DIRECT=1 $CCACHE $COMPILER -DFOO -c test.c -o test.o
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-}
+    # Direct mode should work:
+    files_before=`find $CCACHE_DIR -type f | wc -l`
+    CCACHE_DIRECT=1 CCACHE_READONLY=1 CCACHE_TEMPDIR=/tmp $CCACHE_COMPILE -c test.c
+    files_after=`find $CCACHE_DIR -type f | wc -l`
 
-extrafiles_suite() {
-    ##################################################################
-    # Create some code to compile.
-    cat <<EOF >test.c
-int test;
-EOF
-    echo a >a
-    echo b >b
+    # Leave test dir a nice state after test failure.
+    chmod -R +w $CCACHE_DIR
+
+    if [ $? -ne 0 ]; then
+        test_failed "Failure when compiling test.c read-only"
+    fi
+    if [ $files_after -ne $files_before ]; then
+        test_failed "Read-only mode + direct mode stored files in the cache"
+    fi
+}
 
-    ##################################################################
-    # Test the CCACHE_EXTRAFILES feature.
+# =============================================================================
 
-    testname="cache hit"
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+SUITE_readonly_direct_SETUP() {
+    unset CCACHE_NODIRECT
+
+    generate_code 1 test.c
+}
 
-    testname="cache miss"
-    $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+SUITE_readonly_direct() {
+    # -------------------------------------------------------------------------
+    TEST "Direct hit"
 
-    testname="cache miss a b"
-    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
+    $CCACHE_COMPILE -c test.c -o test.o
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="cache hit a b"
-    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 2
+    CCACHE_READONLY_DIRECT=1 $CCACHE_COMPILE -c test.c -o test.o
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="cache miss a b2"
-    echo b2 >b
-    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 3
-
-    testname="cache hit a b2"
-    CCACHE_EXTRAFILES="a${PATH_DELIM}b" $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (preprocessed)' 3
-    checkstat 'cache miss' 3
-
-    testname="cache miss doesntexist"
-    CCACHE_EXTRAFILES="doesntexist" $CCACHE $COMPILER -c test.c
-    checkstat 'cache hit (preprocessed)' 3
-    checkstat 'cache miss' 3
-    checkstat 'error hashing extra file' 1
+    # -------------------------------------------------------------------------
+    TEST "Direct miss doesn't lead to preprocessed hit"
+
+    $CCACHE_COMPILE -c test.c -o test.o
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_READONLY_DIRECT=1 $CCACHE_COMPILE -DFOO -c test.c -o test.o
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 }
 
-prepare_cleanup_test() {
-    dir=$1
+# =============================================================================
+
+prepare_cleanup_test_dir() {
+    local dir=$1
+
     rm -rf $dir
     mkdir -p $dir
-    i=0
-    while [ $i -lt 10 ]; do
+    for i in $(seq 0 9); do
         printf '%4017s' '' | tr ' ' 'A' >$dir/result$i-4017.o
         touch $dir/result$i-4017.stderr
         touch $dir/result$i-4017.d
         if [ $i -gt 5 ]; then
             backdate $dir/result$i-4017.stderr
         fi
-        i=`expr $i + 1`
     done
     # NUMFILES: 30, TOTALSIZE: 40 KiB, MAXFILES: 0, MAXSIZE: 0
     echo "0 0 0 0 0 0 0 0 0 0 0 30 40 0 0" >$dir/stats
 }
 
-cleanup_suite() {
-    testname="clear"
-    prepare_cleanup_test $CCACHE_DIR/a
-    $CCACHE -C >/dev/null
-    checkfilecount 0 '*.o' $CCACHE_DIR
-    checkfilecount 0 '*.d' $CCACHE_DIR
-    checkfilecount 0 '*.stderr' $CCACHE_DIR
-    checkstat 'files in cache' 0
+SUITE_cleanup() {
+    # -------------------------------------------------------------------------
+    TEST "Clear cache"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
 
-    testname="forced cleanup, no limits"
     $CCACHE -C >/dev/null
-    prepare_cleanup_test $CCACHE_DIR/a
+    expect_file_count 0 '*.o' $CCACHE_DIR
+    expect_file_count 0 '*.d' $CCACHE_DIR
+    expect_file_count 0 '*.stderr' $CCACHE_DIR
+    expect_stat 'files in cache' 0
+    expect_stat 'cleanups performed' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Forced cache cleanup, no limits"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
+
     $CCACHE -F 0 -M 0 >/dev/null
     $CCACHE -c >/dev/null
-    checkfilecount 10 '*.o' $CCACHE_DIR
-    checkfilecount 10 '*.d' $CCACHE_DIR
-    checkfilecount 10 '*.stderr' $CCACHE_DIR
-    checkstat 'files in cache' 30
+    expect_file_count 10 '*.o' $CCACHE_DIR
+    expect_file_count 10 '*.d' $CCACHE_DIR
+    expect_file_count 10 '*.stderr' $CCACHE_DIR
+    expect_stat 'files in cache' 30
+    expect_stat 'cleanups performed' 0
+
+    # -------------------------------------------------------------------------
+    TEST "Forced cache cleanup, file limit"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
 
-    testname="forced cleanup, file limit"
-    $CCACHE -C >/dev/null
-    prepare_cleanup_test $CCACHE_DIR/a
     # (9/10) * 30 * 16 = 432
     $CCACHE -F 432 -M 0 >/dev/null
     $CCACHE -c >/dev/null
     # floor(0.8 * 9) = 7
-    checkfilecount 7 '*.o' $CCACHE_DIR
-    checkfilecount 7 '*.d' $CCACHE_DIR
-    checkfilecount 7 '*.stderr' $CCACHE_DIR
-    checkstat 'files in cache' 21
+    expect_file_count 7 '*.o' $CCACHE_DIR
+    expect_file_count 7 '*.d' $CCACHE_DIR
+    expect_file_count 7 '*.stderr' $CCACHE_DIR
+    expect_stat 'files in cache' 21
+    expect_stat 'cleanups performed' 1
     for i in 0 1 2 3 4 5 9; do
         file=$CCACHE_DIR/a/result$i-4017.o
         if [ ! -f $file ]; then
@@ -1910,22 +2511,27 @@ cleanup_suite() {
         fi
     done
 
-    # Warning: this test is known to fail on filesystems that have
-    # unusual block sizes, including ecryptfs.  The workaround is
-    # to place the test directory elsewhere:
+    # -------------------------------------------------------------------------
+    TEST "Forced cache cleanup, size limit"
+
+    # NOTE: This test is known to fail on filesystems that have unusual block
+    # sizes, including ecryptfs. The workaround is to place the test directory
+    # elsewhere:
+    #
     #     cd /tmp
     #     CCACHE=$DIR/ccache $DIR/test.sh
-    testname="forced cleanup, size limit"
-    $CCACHE -C >/dev/null
-    prepare_cleanup_test $CCACHE_DIR/a
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
+
     # (4/10) * 10 * 4 * 16 = 256
     $CCACHE -F 0 -M 256K >/dev/null
     $CCACHE -c >/dev/null
     # floor(0.8 * 4) = 3
-    checkfilecount 3 '*.o' $CCACHE_DIR
-    checkfilecount 3 '*.d' $CCACHE_DIR
-    checkfilecount 3 '*.stderr' $CCACHE_DIR
-    checkstat 'files in cache' 9
+    expect_file_count 3 '*.o' $CCACHE_DIR
+    expect_file_count 3 '*.d' $CCACHE_DIR
+    expect_file_count 3 '*.stderr' $CCACHE_DIR
+    expect_stat 'files in cache' 9
+    expect_stat 'cleanups performed' 1
     for i in 3 4 5; do
         file=$CCACHE_DIR/a/result$i-4017.o
         if [ ! -f $file ]; then
@@ -1939,37 +2545,43 @@ cleanup_suite() {
         fi
     done
 
-    testname="autocleanup"
-    $CCACHE -C >/dev/null
+    # -------------------------------------------------------------------------
+    TEST "Automatic cache cleanup"
+
     for x in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
-        prepare_cleanup_test $CCACHE_DIR/$x
+        prepare_cleanup_test_dir $CCACHE_DIR/$x
     done
+
     # (9/10) * 30 * 16 = 432
     $CCACHE -F 432 -M 0 >/dev/null
+    expect_file_count 160 '*.o' $CCACHE_DIR
+    expect_file_count 160 '*.d' $CCACHE_DIR
+    expect_file_count 160 '*.stderr' $CCACHE_DIR
+    expect_stat 'files in cache' 480
+
     touch empty.c
-    checkfilecount 160 '*.o' $CCACHE_DIR
-    checkfilecount 160 '*.d' $CCACHE_DIR
-    checkfilecount 160 '*.stderr' $CCACHE_DIR
-    checkstat 'files in cache' 480
-    $CCACHE $COMPILER -c empty.c -o empty.o
+    $CCACHE_COMPILE -c empty.c -o empty.o
     # floor(0.8 * 9) = 7
-    checkfilecount 157 '*.o' $CCACHE_DIR
-    checkfilecount 156 '*.d' $CCACHE_DIR
-    checkfilecount 156 '*.stderr' $CCACHE_DIR
-    checkstat 'files in cache' 469
+    expect_file_count 157 '*.o' $CCACHE_DIR
+    expect_file_count 156 '*.d' $CCACHE_DIR
+    expect_file_count 156 '*.stderr' $CCACHE_DIR
+    expect_stat 'files in cache' 469
+    expect_stat 'cleanups performed' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Cleanup of sibling files"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
 
-    testname="sibling cleanup"
-    $CCACHE -C >/dev/null
-    prepare_cleanup_test $CCACHE_DIR/a
     # (9/10) * 30 * 16 = 432
     $CCACHE -F 432 -M 0 >/dev/null
     backdate $CCACHE_DIR/a/result2-4017.stderr
     $CCACHE -c >/dev/null
     # floor(0.8 * 9) = 7
-    checkfilecount 7 '*.o' $CCACHE_DIR
-    checkfilecount 7 '*.d' $CCACHE_DIR
-    checkfilecount 7 '*.stderr' $CCACHE_DIR
-    checkstat 'files in cache' 21
+    expect_file_count 7 '*.o' $CCACHE_DIR
+    expect_file_count 7 '*.d' $CCACHE_DIR
+    expect_file_count 7 '*.stderr' $CCACHE_DIR
+    expect_stat 'files in cache' 21
     for i in 0 1 3 4 5 8 9; do
         file=$CCACHE_DIR/a/result$i-4017.o
         if [ ! -f $file ]; then
@@ -1983,23 +2595,26 @@ cleanup_suite() {
         fi
     done
 
-    testname="new unknown file"
-    $CCACHE -C >/dev/null
-    prepare_cleanup_test $CCACHE_DIR/a
+    # -------------------------------------------------------------------------
+    TEST "No cleanup of new unknown file"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
+
     touch $CCACHE_DIR/a/abcd.unknown
     $CCACHE -F 0 -M 0 -c >/dev/null # update counters
-    checkstat 'files in cache' 31
+    expect_stat 'files in cache' 31
     # (9/10) * 30 * 16 = 432
     $CCACHE -F 432 -M 0 >/dev/null
     $CCACHE -c >/dev/null
     if [ ! -f $CCACHE_DIR/a/abcd.unknown ]; then
         test_failed "$CCACHE_DIR/a/abcd.unknown removed"
     fi
-    checkstat 'files in cache' 19
+    expect_stat 'files in cache' 19
 
-    testname="old unknown file"
-    $CCACHE -C >/dev/null
-    prepare_cleanup_test $CCACHE_DIR/a
+    # -------------------------------------------------------------------------
+    TEST "Cleanup of old unknown file"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
     # (9/10) * 30 * 16 = 432
     $CCACHE -F 432 -M 0 >/dev/null
     touch $CCACHE_DIR/a/abcd.unknown
@@ -2009,28 +2624,53 @@ cleanup_suite() {
         test_failed "$CCACHE_DIR/a/abcd.unknown not removed"
     fi
 
-    testname="cleanup of tmp files"
-    $CCACHE -C >/dev/null
+    # -------------------------------------------------------------------------
+    TEST "Cleanup of tmp file"
+
+    mkdir -p $CCACHE_DIR/a
     touch $CCACHE_DIR/a/abcd.tmp.efgh
     $CCACHE -c >/dev/null # update counters
-    checkstat 'files in cache' 1
+    expect_stat 'files in cache' 1
     backdate $CCACHE_DIR/a/abcd.tmp.efgh
     $CCACHE -c >/dev/null
     if [ -f $CCACHE_DIR/a/abcd.tmp.efgh ]; then
         test_failed "$CCACHE_DIR/a/abcd.tmp.unknown not removed"
     fi
-    checkstat 'files in cache' 0
+    expect_stat 'files in cache' 0
+
+    # -------------------------------------------------------------------------
+    TEST "No cleanup of .nfs* files"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
 
-    testname="ignore .nfs* files"
-    prepare_cleanup_test $CCACHE_DIR/a
     touch $CCACHE_DIR/a/.nfs0123456789
     $CCACHE -F 0 -M 0 >/dev/null
     $CCACHE -c >/dev/null
-    checkfilecount 1 '.nfs*' $CCACHE_DIR
-    checkstat 'files in cache' 30
+    expect_file_count 1 '.nfs*' $CCACHE_DIR
+    expect_stat 'files in cache' 30
+
+    # -------------------------------------------------------------------------
+    TEST "CCACHE_LIMIT_MULTIPLE"
+
+    prepare_cleanup_test_dir $CCACHE_DIR/a
+
+    # (1/1) * 30 * 16 = 480
+    $CCACHE -F 480 >/dev/null
+    CCACHE_LIMIT_MULTIPLE=0.5 $CCACHE -c >/dev/null
+    expect_stat 'files in cache' 15
+}
+
+# =============================================================================
+
+SUITE_pch_PROBE() {
+    touch pch.h
+    if ! $UNCACHED_COMPILE $SYSROOT -fpch-preprocess pch.h 2>/dev/null \
+            || [ ! -f pch.h.gch ]; then
+        echo "compiler ($($COMPILER --version | head -1)) doesn't support precompiled headers"
+    fi
 }
 
-pch_suite() {
+SUITE_pch_SETUP() {
     unset CCACHE_NODIRECT
 
     cat <<EOF >pch.c
@@ -2044,6 +2684,7 @@ EOF
     cat <<EOF >pch.h
 #include <stdlib.h>
 EOF
+    backdate pch.h
     cat <<EOF >pch2.c
 int main()
 {
@@ -2051,473 +2692,527 @@ int main()
   return 0;
 }
 EOF
+}
 
-    if $COMPILER $SYSROOT -fpch-preprocess pch.h 2>/dev/null && [ -f pch.h.gch ] && $COMPILER $SYSROOT pch.c -o pch; then
-        rm pch.h.gch
-    else
-        echo "Compiler (`$COMPILER --version | head -1`) doesn't support precompiled headers -- not running pch test"
-        return
-    fi
-
-    # clang and gcc handle precompiled headers similarly, but gcc is much more
-    # forgiving with precompiled headers. Both gcc and clang keep an absolute
-    # path reference to the original file except that clang uses that reference
-    # to validate the pch and gcc ignores the reference. Also, clang has an
-    # additional feature: pre-tokenized headers. For these reasons clang should
-    # be tested separately than gcc. clang can only use pch or pth headers on
-    # the command line and not as an #include statement inside a source file.
-
-    if [ $COMPILER_TYPE_CLANG -eq 1 ]; then
-        clang_pch_suite
+SUITE_pch() {
+    # Clang and GCC handle precompiled headers similarly, but GCC is much more
+    # forgiving with precompiled headers. Both GCC and Clang keep an absolute
+    # path reference to the original file except that Clang uses that reference
+    # to validate the pch and GCC ignores the reference. Also, Clang has an
+    # additional feature: pre-tokenized headers. For these reasons, Clang
+    # should be tested differently from GCC. Clang can only use pch or pth
+    # headers on the command line and not as an #include statement inside a
+    # source file.
+
+    if $COMPILER_TYPE_CLANG; then
+        pch_suite_clang
     else
-        gcc_pch_suite
+        pch_suite_gcc
     fi
 }
 
-gcc_pch_suite() {
-    ##################################################################
-    # Tests for creating a .gch without opt-in.
+pch_suite_gcc() {
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, -c, no -o, without opt-in"
 
-    backdate pch.h
+    $CCACHE_COMPILE $SYSROOT -c pch.h
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat "can't use precompiled header" 1
 
-    testname="create .gch, -c, no -o, without opt-in"
-    $CCACHE -zC >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c pch.h
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat "can't use precompiled header" 1
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, no -c, -o, without opt-in"
 
-    testname="create .gch, no -c, -o, without opt-in"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER pch.h -o pch.gch
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
-    checkstat "can't use precompiled header" 1
+    $CCACHE_COMPILE pch.h -o pch.gch
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat "can't use precompiled header" 1
 
-    ##################################################################
-    # Tests for creating a .gch with opt-in.
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, -c, no -o, with opt-in"
 
-    backdate pch.h
+    CCACHE_SLOPPINESS=pch_defines $CCACHE_COMPILE $SYSROOT -c pch.h
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    rm pch.h.gch
 
-    testname="create .gch, -c, no -o, with opt-in"
-    $CCACHE -zC >/dev/null
-    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER $SYSROOT -c pch.h
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    rm -f pch.h.gch
-    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER $SYSROOT -c pch.h
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    CCACHE_SLOPPINESS=pch_defines $CCACHE_COMPILE $SYSROOT -c pch.h
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     if [ ! -f pch.h.gch ]; then
         test_failed "pch.h.gch missing"
     fi
 
-    testname="create .gch, no -c, -o, with opt-in"
-    $CCACHE -Cz >/dev/null
-    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER $SYSROOT pch.h -o pch.gch
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER $SYSROOT pch.h -o pch.gch
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, no -c, -o, with opt-in"
+
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT pch.h -o pch.gch
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT pch.h -o pch.gch
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     if [ ! -f pch.gch ]; then
         test_failed "pch.gch missing"
     fi
-    rm pch.gch
 
-    ##################################################################
-    # Tests for using a .gch.
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, no -fpch-preprocess, #include"
 
-    rm -f pch.h
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
     backdate pch.h.gch
+    rm pch.h
 
-    testname="no -fpch-preprocess, #include"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c pch.c 2>/dev/null
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
+    $CCACHE_COMPILE $SYSROOT -c pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
     # Preprocessor error because GCC can't find the real include file when
     # trying to preprocess:
-    checkstat 'preprocessor error' 1
+    expect_stat 'preprocessor error' 1
 
-    testname="no -fpch-preprocess, -include, no sloppiness"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, no -fpch-preprocess, -include, no sloppiness"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+    rm pch.h
+
+    $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
     # Must enable sloppy time macros:
-    checkstat "can't use precompiled header" 1
+    expect_stat "can't use precompiled header" 1
 
-    testname="no -fpch-preprocess, -include"
-    $CCACHE -Cz >/dev/null
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    testname="-fpch-preprocess, #include, no sloppiness"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, no -fpch-preprocess, -include, sloppiness"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+    rm pch.h
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, -fpch-preprocess, #include, no sloppiness"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+    rm pch.h
+
+    $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
     # Must enable sloppy time macros:
-    checkstat "can't use precompiled header" 1
+    expect_stat "can't use precompiled header" 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, -fpch-preprocess, #include, sloppiness"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+    rm pch.h
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, -fpch-preprocess, #include, file changed"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+    rm pch.h
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="-fpch-preprocess, #include, sloppiness"
-    $CCACHE -Cz >/dev/null
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    testname="-fpch-preprocess, #include, file changed"
     echo "updated" >>pch.h.gch # GCC seems to cope with this...
     backdate pch.h.gch
-    CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
 
-    testname="preprocessor mode"
-    $CCACHE -Cz >/dev/null
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-
-    testname="preprocessor mode, file changed"
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 2
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, preprocessor mode"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+    rm pch.h
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, preprocessor mode, file changed"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+    rm pch.h
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     echo "updated" >>pch.h.gch # GCC seems to cope with this...
     backdate pch.h.gch
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 2
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
 }
 
-clang_pch_suite() {
-    ##################################################################
-    # Tests for creating a .gch.
+pch_suite_clang() {
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, -c, no -o, without opt-in"
 
-    backdate pch.h
+    $CCACHE_COMPILE $SYSROOT -c pch.h
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat "can't use precompiled header" 1
+
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, no -c, -o, without opt-in"
+
+    $CCACHE_COMPILE pch.h -o pch.gch
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
+    expect_stat "can't use precompiled header" 1
 
-    testname="create .gch, -c, no -o"
-    $CCACHE -zC >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c pch.h
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    rm -f pch.h.gch
-    $CCACHE $COMPILER $SYSROOT -c pch.h
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, -c, no -o, with opt-in"
+
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT -c pch.h
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+    rm pch.h.gch
+
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT -c pch.h
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     if [ ! -f pch.h.gch ]; then
         test_failed "pch.h.gch missing"
     fi
 
-    testname="create .gch, no -c, -o"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER $SYSROOT pch.h -o pch.gch
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE $COMPILER $SYSROOT pch.h -o pch.gch
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    # -------------------------------------------------------------------------
+    TEST "Create .gch, no -c, -o, with opt-in"
+
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT pch.h -o pch.gch
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT pch.h -o pch.gch
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     if [ ! -f pch.gch ]; then
         test_failed "pch.gch missing"
     fi
-    rm pch.gch
 
-    ##################################################################
-    # Tests for using a .gch.
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, no -fpch-preprocess, -include, no sloppiness"
 
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
     backdate pch.h.gch
 
-    testname="gch, no -fpch-preprocess, -include, no sloppy time macros"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
+    $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c 2>/dev/null
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
     # Must enable sloppy time macros:
-    checkstat "can't use precompiled header" 1
+    expect_stat "can't use precompiled header" 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, no -fpch-preprocess, -include, sloppiness"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, -fpch-preprocess, -include, file changed"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="gch, no -fpch-preprocess, -include, sloppy time macros"
-    $CCACHE -Cz >/dev/null
-    CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    testname="gch, -fpch-preprocess, -include, file changed"
     echo "updated" >>pch.h.gch # clang seems to cope with this...
     backdate pch.h.gch
-    CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
 
-    testname="gch, preprocessor mode"
-    $CCACHE -Cz >/dev/null
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-
-    testname="gch, preprocessor mode, file changed"
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, preprocessor mode"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .gch, preprocessor mode, file changed"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h
+    backdate pch.h.gch
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     echo "updated" >>pch.h.gch # clang seems to cope with this...
     backdate pch.h.gch
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 2
 
-    rm pch.h.gch
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
 
-    ##################################################################
-    # Tests for creating a .pth.
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
 
-    backdate pch.h
+    # -------------------------------------------------------------------------
+    TEST "Create .pth, -c, -o"
 
-    testname="create .pth, -c, -o"
-    $CCACHE -zC >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c pch.h -o pch.h.pth
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT -c pch.h -o pch.h.pth
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     rm -f pch.h.pth
-    $CCACHE $COMPILER $SYSROOT -c pch.h -o pch.h.pth
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
+
+    CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE_COMPILE $SYSROOT -c pch.h -o pch.h.pth
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
     if [ ! -f pch.h.pth ]; then
         test_failed "pch.h.pth missing"
     fi
 
-    ##################################################################
-    # Tests for using a .pth.
+    # -------------------------------------------------------------------------
+    TEST "Use .pth, no -fpch-preprocess, -include, no sloppiness"
 
+    $UNCACHED_COMPILE $SYSROOT -c pch.h -o pch.h.pth
     backdate pch.h.pth
 
-    testname="pth, no -fpch-preprocess, -include, no sloppy time macros"
-    $CCACHE -Cz >/dev/null
-    $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 0
+    $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 0
     # Must enable sloppy time macros:
-    checkstat "can't use precompiled header" 1
+    expect_stat "can't use precompiled header" 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .pth, no -fpch-preprocess, -include, sloppiness"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h -o pch.h.pth
+    backdate pch.h.pth
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h pch2.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .pth, -fpch-preprocess, -include, file changed"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h -o pch.h.pth
+    backdate pch.h.pth
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    testname="pth, no -fpch-preprocess, -include, sloppy time macros"
-    $CCACHE -Cz >/dev/null
-    CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-
-    testname="pth, -fpch-preprocess, -include, file changed"
     echo "updated" >>pch.h.pth # clang seems to cope with this...
     backdate pch.h.pth
-    CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 1
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 2
 
-    testname="pth, preprocessor mode"
-    $CCACHE -Cz >/dev/null
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-
-    testname="pth, preprocessor mode, file changed"
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 1
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    # -------------------------------------------------------------------------
+    TEST "Use .pth, preprocessor mode"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h -o pch.h.pth
+    backdate pch.h.pth
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
+
+    # -------------------------------------------------------------------------
+    TEST "Use .pth, preprocessor mode, file changed"
+
+    $UNCACHED_COMPILE $SYSROOT -c pch.h -o pch.h.pth
+    backdate pch.h.pth
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
+
     echo "updated" >>pch.h.pth # clang seems to cope with this...
     backdate pch.h.pth
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 2
-    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 2
-    checkstat 'cache miss' 2
-
-    rm pch.h.pth
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 2
+
+    CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines time_macros" $CCACHE_COMPILE $SYSROOT -c -include pch.h -fpch-preprocess pch.c
+    expect_stat 'cache hit (direct)' 0
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
 }
 
-upgrade_suite() {
-    testname="keep maxfiles and maxsize settings"
-    rm -rf $CCACHE_DIR $CCACHE_CONFIGPATH
+# =============================================================================
+
+SUITE_upgrade() {
+    TEST "Keep maxfiles and maxsize settings"
+
+    rm $CCACHE_CONFIGPATH
     mkdir -p $CCACHE_DIR/0
     echo "0 0 0 0 0 0 0 0 0 0 0 0 0 2000 131072" >$CCACHE_DIR/0/stats
-    checkstat 'max files' 32000
-    checkstat 'max cache size' '2.1 GB'
+    expect_stat 'max files' 32000
+    expect_stat 'max cache size' '2.1 GB'
 }
 
-prefix_suite() {
-    testname="prefix"
-    $CCACHE -Cz >/dev/null
-    rm -f prefix.result
-    cat <<'EOF' >prefix-a
-#!/bin/sh
-echo a >>prefix.result
-exec "$@"
-EOF
-    cat <<'EOF' >prefix-b
-#!/bin/sh
-echo b >>prefix.result
-exec "$@"
-EOF
-    chmod +x prefix-a prefix-b
-    cat <<'EOF' >file.c
-int foo;
-EOF
-    PATH=.:$PATH CCACHE_PREFIX="prefix-a prefix-b" $CCACHE $COMPILER -c file.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    checkfile prefix.result "a
-b"
-    PATH=.:$PATH CCACHE_PREFIX="prefix-a prefix-b" $CCACHE $COMPILER -c file.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
-    checkfile prefix.result "a
-b"
-}
+# =============================================================================
 
-buggy_cpp_suite() {
-    testname="buggy_cpp"
-    $CCACHE -Cz >/dev/null
-    cat >buggy-cpp <<EOF
-#!/bin/sh
-CCACHE_DISABLE=1 # If $COMPILER happens to be a ccache symlink...
-export CCACHE_DISABLE
-if echo "\$*" | grep -- -D >/dev/null; then
-  $COMPILER "\$@"
-else
-  # mistreat the preprocessor output in the same way as gcc6 does
-  $COMPILER "\$@" |
-    sed -e '/^# 1 "<command-line>"$/ a # 31 "<command-line>"' \\
-        -e 's/^# 1 "<command-line>" 2$/# 32 "<command-line>" 2/'
-fi
-exit 0
-EOF
-    cat <<'EOF' >file.c
-int foo;
-EOF
-    chmod +x buggy-cpp
-    backdate buggy-cpp
-    $CCACHE ./buggy-cpp -c file.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 0
-    checkstat 'cache miss' 1
-    $CCACHE ./buggy-cpp -DNOT_AFFECTING=1 -c file.c
-    checkstat 'cache hit (direct)' 0
-    checkstat 'cache hit (preprocessed)' 1
-    checkstat 'cache miss' 1
+SUITE_input_charset_PROBE() {
+    touch test.c
+    if ! $UNCACHED_COMPILE -c -finput-charset=latin1 test.c >/dev/null 2>&1; then
+        echo "compiler doesn't support -finput-charset"
+    fi
 }
 
-symlinks_suite() {
-    ##################################################################
-    testname="symlink to source directory"
-
-    mkdir dir
-    cd dir
-    mkdir -p d1/d2
-    echo '#define A "OK"' >d1/h.h
-    cat <<EOF >d1/d2/c.c
-#include <stdio.h>
-#include "../h.h"
-int main() { printf("%s\n", A); }
-EOF
-    echo '#define A "BUG"' >h.h
-    ln -s d1/d2 d3
-
-    CCACHE_BASEDIR=/ $CCACHE $COMPILER -c $PWD/d3/c.c
-    $COMPILER -c $PWD/d3/c.c
-    $COMPILER c.o -o c
-    result=$(./c)
-    if [ "$result" != OK ]; then
-        test_failed "Incorrect header file used"
-    fi
+SUITE_input_charset() {
+    # -------------------------------------------------------------------------
+    TEST "-finput-charset"
 
-    cd ..
-    rm -rf dir
+    printf '#include <wchar.h>\nwchar_t foo[] = L"\xbf";\n' >latin1.c
 
-    ##################################################################
-    testname="symlink to source file"
+    $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
+    expect_stat 'cache hit (preprocessed)' 0
+    expect_stat 'cache miss' 1
 
-    mkdir dir
-    cd dir
-    mkdir d
-    echo '#define A "BUG"' >d/h.h
-    cat <<EOF >d/c.c
-#include <stdio.h>
-#include "h.h"
-int main() { printf("%s\n", A); }
-EOF
-    echo '#define A "OK"' >h.h
-    ln -s d/c.c c.c
+    $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 1
 
-    CCACHE_BASEDIR=/ $CCACHE $COMPILER -c $PWD/c.c
-    $COMPILER c.o -o c
-    result=$(./c)
-    if [ "$result" != OK ]; then
-        test_failed "Incorrect header file used"
-    fi
+    CCACHE_NOCPP2=1 $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
+    expect_stat 'cache hit (preprocessed)' 1
+    expect_stat 'cache miss' 2
 
-    cd ..
-    rm -rf dir
+    CCACHE_NOCPP2=1 $CCACHE_COMPILE -c -finput-charset=latin1 latin1.c
+    expect_stat 'cache hit (preprocessed)' 2
+    expect_stat 'cache miss' 2
 }
 
-######################################################################
+# =============================================================================
 # main program
 
 if pwd | grep '[^A-Za-z0-9/.,=_%+-]' >/dev/null 2>&1; then
@@ -2528,7 +3223,6 @@ EOF
     exit 1
 fi
 
-suites="$*"
 if [ -n "$CC" ]; then
     COMPILER="$CC"
 else
@@ -2538,20 +3232,23 @@ if [ -z "$CCACHE" ]; then
     CCACHE=`pwd`/ccache
 fi
 
-# save the type of compiler because some test may not work on all compilers
-COMPILER_TYPE_CLANG=0
-COMPILER_TYPE_GCC=0
+COMPILER_TYPE_CLANG=false
+COMPILER_TYPE_GCC=false
 
-COMPILER_USES_LLVM=0
-HOST_OS_APPLE=0
+COMPILER_USES_LLVM=false
+COMPILER_USES_MINGW=false
+
+HOST_OS_APPLE=false
+HOST_OS_LINUX=false
+HOST_OS_WINDOWS=false
 
 compiler_version="`$COMPILER --version 2>&1 | head -1`"
 case $compiler_version in
     *gcc*|*g++*|2.95*)
-        COMPILER_TYPE_GCC=1
+        COMPILER_TYPE_GCC=true
         ;;
     *clang*)
-        COMPILER_TYPE_CLANG=1
+        COMPILER_TYPE_CLANG=true
         ;;
     *)
         echo "WARNING: Compiler $COMPILER not supported (version: $compiler_version) -- not running tests" >&2
@@ -2561,32 +3258,32 @@ esac
 
 case $compiler_version in
     *llvm*|*LLVM*)
-        COMPILER_USES_LLVM=1
+        COMPILER_USES_LLVM=true
+        ;;
+    *MINGW*|*mingw*)
+        COMPILER_USES_MINGW=true
         ;;
 esac
 
-host_os="`uname -s`"
-case $host_os in
+case $(uname -s) in
+    *MINGW*|*mingw*)
+        HOST_OS_WINDOWS=true
+        ;;
     *Darwin*)
-        HOST_OS_APPLE=1
+        HOST_OS_APPLE=true
+        ;;
+    *Linux*)
+        HOST_OS_LINUX=true
         ;;
 esac
 
-TESTDIR=testdir.$$
-rm -rf $TESTDIR
-mkdir $TESTDIR
-cd $TESTDIR || exit 1
-
-CCACHE_DIR=`pwd`/.ccache
-export CCACHE_DIR
-CCACHE_LOGFILE=`pwd`/ccache.log
-export CCACHE_LOGFILE
-CCACHE_CONFIGPATH=`pwd`/ccache.conf
-export CCACHE_CONFIGPATH
-touch $CCACHE_CONFIGPATH
-
+if $HOST_OS_WINDOWS; then
+    PATH_DELIM=";"
+else
+    PATH_DELIM=":"
+fi
 
-if [ $HOST_OS_APPLE -eq 1 ]; then
+if $HOST_OS_APPLE; then
     # Grab the developer directory from the environment or try xcode-select
     if [ "$XCODE_DEVELOPER_DIR" = "" ]; then
       XCODE_DEVELOPER_DIR=`xcode-select --print-path`
@@ -2613,43 +3310,46 @@ fi
 
 # ---------------------------------------
 
+TESTDIR=testdir.$$
+ABS_TESTDIR=$PWD/$TESTDIR
+rm -rf $TESTDIR
+mkdir $TESTDIR
+cd $TESTDIR || exit 1
+
+# ---------------------------------------
+
 all_suites="
 base
-link          !win32
+nocpp2
+multi_arch
+serialize_diagnostics
+debug_prefix_map
+masquerading
 hardlink
-nlevels4
-nlevels1
-basedir       !win32
 direct
+basedir
 compression
 readonly
 readonly_direct
-extrafiles
 cleanup
 pch
-symlinks
 upgrade
-prefix
-buggy_cpp
+input_charset
 "
 
-case $host_os in
-    *MINGW*|*mingw*)
-        export CCACHE_DETECT_SHEBANG
-        CCACHE_DETECT_SHEBANG=1
-        PATH_DELIM=";"
-        all_suites="`echo "$all_suites" | grep -v '!win32'`"
-        ;;
-    *)
-        PATH_DELIM=":"
-        all_suites="`echo "$all_suites" | cut -d' ' -f1`"
-        ;;
-esac
+compiler_location=$(which $COMPILER)
+if [ "$compiler_location" = "$COMPILER" ]; then
+    echo "Compiler:         $COMPILER"
+else
+    echo "Compiler:         $COMPILER ($(which $COMPILER))"
+fi
+echo "Compiler version: $($COMPILER --version | head -n 1)"
+echo
 
-echo compiler: `which $COMPILER`
-echo version: `$COMPILER --version`
-echo test dir: $TESTDIR
+VERBOSE=false
+[ "$1" = "-v" ] && { VERBOSE=true; shift; }
 
+suites="$*"
 if [ -z "$suites" ]; then
     suites="$all_suites"
 fi
@@ -2658,9 +3358,7 @@ for suite in $suites; do
     run_suite $suite
 done
 
-# ---------------------------------------
-
-cd ..
-rm -rf $TESTDIR
-echo test done - OK
+cd /
+rm -rf $ABS_TESTDIR
+green PASSED
 exit 0
index c15e2f0..ccaa785 100644 (file)
@@ -1,25 +1,25 @@
-/*
- * Copyright (C) 2010-2014 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-#include "ccache.h"
-#include "test/framework.h"
+#include "../ccache.h"
+#include "framework.h"
 #include "util.h"
 
+#include <float.h>
+#include <math.h>
 #if defined(HAVE_TERMIOS_H)
 #define USE_COLOR
 #include <termios.h>
@@ -36,8 +36,8 @@ static char *dir_before_test;
 static int verbose;
 
 static const char COLOR_END[] = "\x1b[m";
-static const char COLOR_GREEN[] = "\x1b[32m";
-static const char COLOR_RED[] = "\x1b[31m";
+static const char COLOR_GREEN[] = "\x1b[1;32m";
+static const char COLOR_RED[] = "\x1b[1;31m";
 
 #define COLOR(tty, color) ((tty) ? COLOR_ ## color : "")
 
@@ -65,7 +65,7 @@ cct_run(suite_fn *suites, int verbose_output)
        suite_fn *suite;
        int tty = is_tty(1);
 
-       x_unsetenv("GCC_COLORS"); /* Avoid confusing argument processing tests. */
+       x_unsetenv("GCC_COLORS"); // Avoid confusing argument processing tests.
        verbose = verbose_output;
 
        for (suite = suites; *suite; suite++) {
@@ -73,7 +73,7 @@ cct_run(suite_fn *suites, int verbose_output)
                while (true) {
                        test_index = (*suite)(test_index + 1);
                        if (test_index == 0) {
-                               /* We have reached the end of the suite. */
+                               // We have reached the end of the suite.
                                break;
                        }
                }
@@ -173,6 +173,22 @@ cct_check_failed(const char *file, int line, const char *what,
 }
 
 bool
+cct_check_float_eq(const char *file, int line, const char *expression,
+                   double expected, double actual)
+{
+       if (fabs(expected -  actual) < DBL_EPSILON) {
+               cct_check_passed(file, line, expression);
+               return true;
+       } else {
+               char *exp_str = format("%.1f", (double)expected);
+               char *act_str = format("%.1f", (double)actual);
+               cct_check_failed(file, line, expression, exp_str, act_str);
+               free(exp_str);
+               free(act_str);
+               return false;
+       }
+}
+bool
 cct_check_int_eq(const char *file, int line, const char *expression,
                  int64_t expected, int64_t actual)
 {
@@ -180,7 +196,7 @@ cct_check_int_eq(const char *file, int line, const char *expression,
                cct_check_passed(file, line, expression);
                return true;
        } else {
-#ifdef HAVE_LONG_LONG
+#if defined(HAVE_LONG_LONG) && !defined(__MINGW32__)
                char *exp_str = format("%lld", (long long)expected);
                char *act_str = format("%lld", (long long)actual);
 #else
@@ -262,8 +278,12 @@ cct_chdir(const char *path)
 void
 cct_wipe(const char *path)
 {
-       /* TODO: rewrite using traverse(). */
+       // TODO: rewrite using traverse().
+#ifndef __MINGW32__
        char *command = format("rm -rf %s", path);
+#else
+       char *command = format("rd /s /q %s", path);
+#endif
        if (system(command) != 0) {
                perror(command);
        }
index fc9ff8d..d1aa6a7 100644 (file)
@@ -1,27 +1,25 @@
-/*
- * Copyright (C) 2010-2012 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #ifndef TEST_FRAMEWORK_H
 #define TEST_FRAMEWORK_H
 
-#include "ccache.h"
+#include "../ccache.h"
 
-/*****************************************************************************/
+// ============================================================================
 
 #define TEST_SUITE(name) \
        unsigned suite_##name(unsigned _start_point) \
@@ -29,7 +27,7 @@
                unsigned _test_counter = 0; \
                cct_suite_begin(#name); \
                { \
-                       /* Empty due to macro trickery. */
+                       // Empty due to macro trickery.
 
 #define TEST(name) \
                        cct_test_end(); \
@@ -46,7 +44,7 @@
                return 0; /* We have reached the end. */ \
        }
 
-/*****************************************************************************/
+// ============================================================================
 
 #define CHECKM(assertion, message) \
        do { \
@@ -72,7 +70,7 @@
                } \
        } while (false)
 
-/*****************************************************************************/
+// ============================================================================
 
 #define CHECK_INT_EQ(expected, actual) \
        do { \
                } \
        } while (false)
 
-/*****************************************************************************/
+// ============================================================================
+
+#define CHECK_FLOAT_EQ(expected, actual) \
+       do { \
+               if (!cct_check_float_eq(__FILE__, __LINE__, #actual, (expected), \
+                                     (actual))) { \
+                       cct_test_end(); \
+                       cct_suite_end(); \
+                       return _test_counter; \
+               } \
+       } while (false)
+
+// ============================================================================
 
 #define CHECK_STR_EQ(expected, actual) \
        CHECK_POINTER_EQ_BASE(str, expected, actual, false, false)
 #define CHECK_STR_EQ_FREE12(expected, actual) \
        CHECK_POINTER_EQ_BASE(str, expected, actual, true, true)
 
-/*****************************************************************************/
+// ============================================================================
 
 #define CHECK_ARGS_EQ(expected, actual) \
        CHECK_POINTER_EQ_BASE(args, expected, actual, false, false)
 #define CHECK_ARGS_EQ_FREE12(expected, actual) \
        CHECK_POINTER_EQ_BASE(args, expected, actual, true, true)
 
-/*****************************************************************************/
+// ============================================================================
 
 typedef unsigned (*suite_fn)(unsigned);
 int cct_run(suite_fn *suites, int verbose);
@@ -124,6 +134,8 @@ void cct_test_end(void);
 void cct_check_passed(const char *file, int line, const char *assertion);
 void cct_check_failed(const char *file, int line, const char *assertion,
                       const char *expected, const char *actual);
+bool cct_check_float_eq(const char *file, int line, const char *expression,
+                        double expected, double actual);
 bool cct_check_int_eq(const char *file, int line, const char *expression,
                       int64_t expected, int64_t actual);
 bool cct_check_str_eq(const char *file, int line, const char *expression,
index 15943cf..965ffed 100644 (file)
@@ -1,32 +1,31 @@
-/* Mode: -*-c-*- */
-/*
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-#include "test/framework.h"
+#include "framework.h"
 #ifdef HAVE_GETOPT_LONG
 #include <getopt.h>
 #else
-#include "getopt_long.h"
+#include "../getopt_long.h"
 #endif
 
+// *INDENT-OFF* disable uncrustify
 #define SUITE(name) unsigned suite_ ## name(unsigned);
-#include "test/suites.h"
+#include "suites.h"
 #undef SUITE
+// *INDENT-ON* enable uncrustify
 
 const char USAGE_TEXT[] =
   "Usage:\n"
@@ -41,7 +40,7 @@ main(int argc, char **argv)
 {
        suite_fn suites[] = {
 #define SUITE(name) &suite_ ## name,
-#include "test/suites.h"
+#include "suites.h"
 #undef SUITE
                NULL
        };
index d0a3bac..6b11dc4 100644 (file)
@@ -1,28 +1,24 @@
-/*
- * Copyright (C) 2010, 2012 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This file contains tests for the functions operating on struct args.
- */
-
-#include "ccache.h"
-#include "test/framework.h"
-#include "test/util.h"
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// This file contains tests for the functions operating on struct args.
+
+#include "../ccache.h"
+#include "framework.h"
+#include "util.h"
 
 TEST_SUITE(args)
 
@@ -78,7 +74,11 @@ TEST(args_init_from_gcc_atfile)
        CHECK_STR_EQ("fourth", args->argv[3]);
        CHECK_STR_EQ("fif th", args->argv[4]);
        CHECK_STR_EQ("si'x\" th", args->argv[5]);
+#ifndef _WIN32
        CHECK_STR_EQ("seve\nth", args->argv[6]);
+#else
+       CHECK_STR_EQ("seve\r\nth", args->argv[6]);
+#endif
        CHECK(!args->argv[7]);
        args_free(args);
 }
index a1b6ecb..7dba493 100644 (file)
@@ -1,32 +1,65 @@
-/*
- * Copyright (C) 2010-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This file contains tests for the processing of compiler arguments.
- */
-
-#include "ccache.h"
-#include "conf.h"
-#include "test/framework.h"
-#include "test/util.h"
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// This file contains tests for the processing of compiler arguments.
+
+#include "../ccache.h"
+#include "../conf.h"
+#include "framework.h"
+#include "util.h"
 
 extern struct conf *conf;
 
+static char *
+get_root(void)
+{
+#ifndef _WIN32
+       return x_strdup("/");
+#else
+       char volume[4]; // "C:\"
+       GetVolumePathName(get_cwd(), volume, sizeof(volume));
+       return x_strdup(volume);
+#endif
+}
+
+static char *
+get_posix_path(char *path)
+{
+#ifndef _WIN32
+       return x_strdup(path);
+#else
+       char *posix;
+       char *p;
+
+       // /-escape volume.
+       if (path[0] >= 'A' && path[0] <= 'Z' && path[1] == ':') {
+               posix = format("/%s", path);
+       } else {
+               posix = x_strdup(path);
+       }
+       // Convert slashes.
+       for (p = posix; *p; p++) {
+               if (*p == '\\') {
+                       *p = '/';
+               }
+       }
+       return posix;
+#endif
+}
+
 TEST_SUITE(argument_processing)
 
 TEST(dash_E_should_result_in_called_for_preprocessing)
@@ -57,7 +90,7 @@ TEST(dependency_flags_should_only_be_sent_to_the_preprocessor)
 {
 #define CMD \
        "cc -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2" \
-       " -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf"
+       " -Wp,-MD,wpmd -Wp,-MMD,wpmmd"
        struct args *orig = args_init_from_string(CMD " -c foo.c -o foo.o");
        struct args *exp_cpp = args_init_from_string(CMD);
 #undef CMD
@@ -72,16 +105,15 @@ TEST(dependency_flags_should_only_be_sent_to_the_preprocessor)
        args_free(orig);
 }
 
-TEST(preprocessor_only_flags_should_only_be_sent_to_the_preprocessor)
+TEST(cpp_only_flags_to_preprocessor_if_run_second_cpp_is_false)
 {
 #define CMD \
        "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \
        " -include test.h -include-pch test.pch -iprefix . -iquote ." \
        " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \
        " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \
-       " -fno-working-directory -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2" \
-       " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt" \
-       " -Wp,-MQ,wpmq -Wp,-MF,wpf"
+       " -fno-working-directory -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 " \
+       " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd"
        struct args *orig = args_init_from_string(CMD " -c foo.c -o foo.o");
        struct args *exp_cpp = args_init_from_string(CMD);
 #undef CMD
@@ -89,6 +121,33 @@ TEST(preprocessor_only_flags_should_only_be_sent_to_the_preprocessor)
        struct args *act_cpp = NULL, *act_cc = NULL;
        create_file("foo.c", "");
 
+       conf->run_second_cpp = false;
+       CHECK(cc_process_args(orig, &act_cpp, &act_cc));
+       CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
+       CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
+
+       args_free(orig);
+}
+
+TEST(cpp_only_flags_to_preprocessor_and_compiler_if_run_second_cpp_is_true)
+{
+#define CMD \
+       "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \
+       " -include test.h -include-pch test.pch -iprefix . -iquote ." \
+       " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \
+       " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \
+       " -fno-working-directory"
+#define DEP_OPTS \
+       " -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 " \
+       " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd"
+       struct args *orig = args_init_from_string(CMD DEP_OPTS " -c foo.c -o foo.o");
+       struct args *exp_cpp = args_init_from_string(CMD DEP_OPTS);
+       struct args *exp_cc = args_init_from_string(CMD " -c");
+#undef CMD
+       struct args *act_cpp = NULL, *act_cc = NULL;
+       create_file("foo.c", "");
+
+       conf->run_second_cpp = true;
        CHECK(cc_process_args(orig, &act_cpp, &act_cc));
        CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
        CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
@@ -122,10 +181,11 @@ TEST(sysroot_should_be_rewritten_if_basedir_is_used)
 
        create_file("foo.c", "");
        free(conf->base_dir);
-       conf->base_dir = x_strdup("/");
+       conf->base_dir = get_root();
        current_working_dir = get_cwd();
        arg_string = format("cc --sysroot=%s/foo -c foo.c", current_working_dir);
        orig = args_init_from_string(arg_string);
+       free(arg_string);
 
        CHECK(cc_process_args(orig, &act_cpp, &act_cc));
        CHECK_STR_EQ(act_cpp->argv[1], "--sysroot=./foo");
@@ -261,12 +321,14 @@ TEST(fprofile_flag_with_existing_dir_should_be_rewritten_to_real_path)
        struct args *exp_cpp = args_init_from_string("gcc");
        struct args *exp_cc = args_init_from_string("gcc");
        struct args *act_cpp = NULL, *act_cc = NULL;
-       char *s;
+       char *s, *path;
 
        create_file("foo.c", "");
        mkdir("some", 0777);
        mkdir("some/dir", 0777);
-       s = format("-fprofile-generate=%s", x_realpath("some/dir"));
+       path = x_realpath("some/dir");
+       s = format("-fprofile-generate=%s", path);
+       free(path);
        args_add(exp_cpp, s);
        args_add(exp_cc, s);
        args_add(exp_cc, "-c");
@@ -298,4 +360,81 @@ TEST(fprofile_flag_with_nonexisting_dir_should_not_be_rewritten)
        args_free(orig);
 }
 
+TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used)
+{
+       extern char *current_working_dir;
+       char *arg_string;
+       struct args *orig;
+       struct args *act_cpp = NULL, *act_cc = NULL;
+
+       create_file("foo.c", "");
+       free(conf->base_dir);
+       conf->base_dir = get_root();
+       current_working_dir = get_cwd();
+       arg_string = format("cc -isystem %s/foo -c foo.c", current_working_dir);
+       orig = args_init_from_string(arg_string);
+       free(arg_string);
+
+       CHECK(cc_process_args(orig, &act_cpp, &act_cc));
+       CHECK_STR_EQ("./foo", act_cpp->argv[2]);
+
+       args_free(orig);
+       args_free(act_cpp);
+       args_free(act_cc);
+}
+
+TEST(isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used)
+{
+       extern char *current_working_dir;
+       char *cwd;
+       char *arg_string;
+       struct args *orig;
+       struct args *act_cpp = NULL, *act_cc = NULL;
+
+       create_file("foo.c", "");
+       free(conf->base_dir);
+       conf->base_dir = x_strdup("/"); // posix
+       current_working_dir = get_cwd();
+       // Windows path doesn't work concatenated.
+       cwd = get_posix_path(current_working_dir);
+       arg_string = format("cc -isystem%s/foo -c foo.c", cwd);
+       orig = args_init_from_string(arg_string);
+       free(arg_string);
+
+       CHECK(cc_process_args(orig, &act_cpp, &act_cc));
+       CHECK_STR_EQ("-isystem./foo", act_cpp->argv[1]);
+
+       free(cwd);
+       args_free(orig);
+       args_free(act_cpp);
+       args_free(act_cc);
+}
+
+TEST(I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used)
+{
+       extern char *current_working_dir;
+       char *cwd;
+       char *arg_string;
+       struct args *orig;
+       struct args *act_cpp = NULL, *act_cc = NULL;
+
+       create_file("foo.c", "");
+       free(conf->base_dir);
+       conf->base_dir = x_strdup("/"); // posix
+       current_working_dir = get_cwd();
+       // Windows path doesn't work concatenated.
+       cwd = get_posix_path(current_working_dir);
+       arg_string = format("cc -I%s/foo -c foo.c", cwd);
+       orig = args_init_from_string(arg_string);
+       free(arg_string);
+
+       CHECK(cc_process_args(orig, &act_cpp, &act_cc));
+       CHECK_STR_EQ("-I./foo", act_cpp->argv[1]);
+
+       free(cwd);
+       args_free(orig);
+       args_free(act_cpp);
+       args_free(act_cc);
+}
+
 TEST_SUITE_END
index 6c17c3d..dc1e74c 100644 (file)
@@ -1,28 +1,24 @@
-/*
- * Copyright (C) 2010, 2012 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This file contains tests for the compopt_* functions.
- */
-
-#include "ccache.h"
-#include "compopt.h"
-#include "test/framework.h"
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// This file contains tests for the compopt_* functions.
+
+#include "../ccache.h"
+#include "../compopt.h"
+#include "framework.h"
 
 TEST_SUITE(compopt)
 
index 780d64f..ea43e2e 100644 (file)
@@ -1,26 +1,24 @@
-/*
- * Copyright (C) 2011-2014 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "conf.h"
-#include "test/framework.h"
-#include "test/util.h"
-
-#define N_CONFIG_ITEMS 27
+// Copyright (C) 2011-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "../conf.h"
+#include "framework.h"
+#include "util.h"
+
+#define N_CONFIG_ITEMS 31
 static struct {
        char *descr;
        const char *origin;
@@ -63,16 +61,20 @@ TEST(conf_create)
        CHECK(!conf->disable);
        CHECK_STR_EQ("", conf->extra_files_to_hash);
        CHECK(!conf->hard_link);
-       CHECK(!conf->hash_dir);
+       CHECK(conf->hash_dir);
+       CHECK_STR_EQ("", conf->ignore_headers_in_manifest);
+       CHECK(!conf->keep_comments_cpp);
+       CHECK_FLOAT_EQ(0.8f, conf->limit_multiple);
        CHECK_STR_EQ("", conf->log_file);
        CHECK_INT_EQ(0, conf->max_files);
        CHECK_INT_EQ((uint64_t)5 * 1000 * 1000 * 1000, conf->max_size);
        CHECK_STR_EQ("", conf->path);
        CHECK_STR_EQ("", conf->prefix_command);
+       CHECK_STR_EQ("", conf->prefix_command_cpp);
        CHECK(!conf->read_only);
        CHECK(!conf->read_only_direct);
        CHECK(!conf->recache);
-       CHECK(!conf->run_second_cpp);
+       CHECK(conf->run_second_cpp);
        CHECK_INT_EQ(0, conf->sloppiness);
        CHECK(conf->stats);
        CHECK_STR_EQ("", conf->temporary_dir);
@@ -90,7 +92,11 @@ TEST(conf_read_valid_config)
        CHECK_STR_EQ("rabbit", user);
        create_file(
          "ccache.conf",
+#ifndef _WIN32
          "base_dir =  /$USER/foo/${USER} \n"
+#else
+         "base_dir = C:/$USER/foo/${USER}\n"
+#endif
          "cache_dir=\n"
          "cache_dir = $USER$/${USER}/.ccache\n"
          "\n"
@@ -106,25 +112,33 @@ TEST(conf_read_valid_config)
          "disable = true\n"
          "extra_files_to_hash = a:b c:$USER\n"
          "hard_link = true\n"
-         "hash_dir = true\n"
+         "hash_dir = false\n"
+         "ignore_headers_in_manifest = a:b/c\n"
+         "keep_comments_cpp = true\n"
+         "limit_multiple = 1.0\n"
          "log_file = $USER${USER} \n"
          "max_files = 17\n"
          "max_size = 123M\n"
          "path = $USER.x\n"
          "prefix_command = x$USER\n"
+         "prefix_command_cpp = y\n"
          "read_only = true\n"
          "read_only_direct = true\n"
          "recache = true\n"
-         "run_second_cpp = true\n"
-         "sloppiness =     file_macro   ,time_macros,  include_file_mtime,include_file_ctime,file_stat_matches, pch_defines  \n"
+         "run_second_cpp = false\n"
+         "sloppiness =     file_macro   ,time_macros,  include_file_mtime,include_file_ctime,file_stat_matches,pch_defines ,  no_system_headers  \n"
          "stats = false\n"
          "temporary_dir = ${USER}_foo\n"
          "umask = 777\n"
-         "unify = true"); /* Note: no newline */
+         "unify = true"); // Note: no newline.
        CHECK(conf_read(conf, "ccache.conf", &errmsg));
        CHECK(!errmsg);
 
+#ifndef _WIN32
        CHECK_STR_EQ_FREE1(format("/%s/foo/%s", user, user), conf->base_dir);
+#else
+       CHECK_STR_EQ_FREE1(format("C:/%s/foo/%s", user, user), conf->base_dir);
+#endif
        CHECK_STR_EQ_FREE1(format("%s$/%s/.ccache", user, user), conf->cache_dir);
        CHECK_INT_EQ(4, conf->cache_dir_levels);
        CHECK_STR_EQ("foo", conf->compiler);
@@ -136,19 +150,24 @@ TEST(conf_read_valid_config)
        CHECK(conf->disable);
        CHECK_STR_EQ_FREE1(format("a:b c:%s", user), conf->extra_files_to_hash);
        CHECK(conf->hard_link);
-       CHECK(conf->hash_dir);
+       CHECK(!conf->hash_dir);
+       CHECK_STR_EQ("a:b/c", conf->ignore_headers_in_manifest);
+       CHECK(conf->keep_comments_cpp);
+       CHECK_FLOAT_EQ(1.0, conf->limit_multiple);
        CHECK_STR_EQ_FREE1(format("%s%s", user, user), conf->log_file);
        CHECK_INT_EQ(17, conf->max_files);
        CHECK_INT_EQ(123 * 1000 * 1000, conf->max_size);
        CHECK_STR_EQ_FREE1(format("%s.x", user), conf->path);
        CHECK_STR_EQ_FREE1(format("x%s", user), conf->prefix_command);
+       CHECK_STR_EQ("y", conf->prefix_command_cpp);
        CHECK(conf->read_only);
        CHECK(conf->read_only_direct);
        CHECK(conf->recache);
-       CHECK(conf->run_second_cpp);
+       CHECK(!conf->run_second_cpp);
        CHECK_INT_EQ(SLOPPY_INCLUDE_FILE_MTIME|SLOPPY_INCLUDE_FILE_CTIME|
                     SLOPPY_FILE_MACRO|SLOPPY_TIME_MACROS|
-                    SLOPPY_FILE_STAT_MATCHES|SLOPPY_PCH_DEFINES,
+                    SLOPPY_FILE_STAT_MATCHES|SLOPPY_NO_SYSTEM_HEADERS|
+                    SLOPPY_PCH_DEFINES,
                     conf->sloppiness);
        CHECK(!conf->stats);
        CHECK_STR_EQ_FREE1(format("%s_foo", user), conf->temporary_dir);
@@ -205,7 +224,7 @@ TEST(conf_read_invalid_env_string)
        CHECK(!conf_read(conf, "ccache.conf", &errmsg));
        CHECK_STR_EQ_FREE2("ccache.conf:1: syntax error: missing '}' after \"foo\"",
                           errmsg);
-       /* Other cases tested in test_util.c. */
+       // Other cases tested in test_util.c.
        conf_free(conf);
 }
 
@@ -227,7 +246,7 @@ TEST(conf_read_invalid_size)
        CHECK(!conf_read(conf, "ccache.conf", &errmsg));
        CHECK_STR_EQ_FREE2("ccache.conf:1: invalid size: \"foo\"",
                           errmsg);
-       /* Other cases tested in test_util.c. */
+       // Other cases tested in test_util.c.
        conf_free(conf);
 }
 
@@ -357,19 +376,24 @@ TEST(conf_print_items)
                true,
                "efth",
                true,
+               .hash_dir = false,
+               "ihim",
                true,
+               0.0,
                "lf",
                4711,
                98.7 * 1000 * 1000,
                "p",
                "pc",
+               "pcc",
                true,
                true,
                true,
-               true,
+               .run_second_cpp = false,
                SLOPPY_FILE_MACRO|SLOPPY_INCLUDE_FILE_MTIME|
                SLOPPY_INCLUDE_FILE_CTIME|SLOPPY_TIME_MACROS|
-               SLOPPY_FILE_STAT_MATCHES|SLOPPY_PCH_DEFINES,
+               SLOPPY_FILE_STAT_MATCHES|SLOPPY_PCH_DEFINES|
+               SLOPPY_NO_SYSTEM_HEADERS,
                false,
                "td",
                022,
@@ -380,7 +404,11 @@ TEST(conf_print_items)
 
        conf.item_origins = x_malloc(N_CONFIG_ITEMS * sizeof(char *));
        for (i = 0; i < N_CONFIG_ITEMS; ++i) {
+#ifndef __MINGW32__
                conf.item_origins[i] = format("origin%zu", i);
+#else
+               conf.item_origins[i] = format("origin%u", (unsigned) i);
+#endif
        }
 
        conf_print_items(&conf, conf_item_receiver, NULL);
@@ -397,19 +425,24 @@ TEST(conf_print_items)
        CHECK_STR_EQ("disable = true", received_conf_items[n++].descr);
        CHECK_STR_EQ("extra_files_to_hash = efth", received_conf_items[n++].descr);
        CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr);
-       CHECK_STR_EQ("hash_dir = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("hash_dir = false", received_conf_items[n++].descr);
+       CHECK_STR_EQ("ignore_headers_in_manifest = ihim",
+                    received_conf_items[n++].descr);
+       CHECK_STR_EQ("keep_comments_cpp = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("limit_multiple = 0.0", received_conf_items[n++].descr);
        CHECK_STR_EQ("log_file = lf", received_conf_items[n++].descr);
        CHECK_STR_EQ("max_files = 4711", received_conf_items[n++].descr);
        CHECK_STR_EQ("max_size = 98.7M", received_conf_items[n++].descr);
        CHECK_STR_EQ("path = p", received_conf_items[n++].descr);
        CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++].descr);
+       CHECK_STR_EQ("prefix_command_cpp = pcc", received_conf_items[n++].descr);
        CHECK_STR_EQ("read_only = true", received_conf_items[n++].descr);
        CHECK_STR_EQ("read_only_direct = true", received_conf_items[n++].descr);
        CHECK_STR_EQ("recache = true", received_conf_items[n++].descr);
-       CHECK_STR_EQ("run_second_cpp = true", received_conf_items[n++].descr);
+       CHECK_STR_EQ("run_second_cpp = false", received_conf_items[n++].descr);
        CHECK_STR_EQ("sloppiness = file_macro, include_file_mtime,"
                     " include_file_ctime, time_macros, pch_defines,"
-                    " file_stat_matches",
+                    " file_stat_matches, no_system_headers",
                     received_conf_items[n++].descr);
        CHECK_STR_EQ("stats = false", received_conf_items[n++].descr);
        CHECK_STR_EQ("temporary_dir = td", received_conf_items[n++].descr);
@@ -417,8 +450,12 @@ TEST(conf_print_items)
        CHECK_STR_EQ("unify = true", received_conf_items[n++].descr);
 
        for (i = 0; i < N_CONFIG_ITEMS; ++i) {
+#ifndef __MINGW32__
                char *expected = format("origin%zu", i);
-               CHECK_STR_EQ(expected, received_conf_items[i].origin);
+#else
+               char *expected = format("origin%u", (unsigned) i);
+#endif
+               CHECK_STR_EQ_FREE1(expected, received_conf_items[i].origin);
        }
 
        free_received_conf_items();
index df12a8b..1705b76 100644 (file)
@@ -1,25 +1,23 @@
-/*
- * Copyright (C) 2010-2011 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "ccache.h"
-#include "counters.h"
-#include "test/framework.h"
-#include "test/util.h"
+// Copyright (C) 2010-2011 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "../ccache.h"
+#include "../counters.h"
+#include "framework.h"
+#include "util.h"
 
 TEST_SUITE(counters)
 
index 46f65db..3fe3d69 100644 (file)
@@ -1,27 +1,23 @@
-/*
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This file contains tests for functions in hash.c.
- */
-
-#include "ccache.h"
-#include "test/framework.h"
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// This file contains tests for functions in hash.c.
+
+#include "../ccache.h"
+#include "framework.h"
 
 TEST_SUITE(hash)
 
index 9b6fd34..22930c9 100644 (file)
@@ -1,29 +1,25 @@
-/*
- * Copyright (C) 2010, 2012 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This file contains tests for functions in hashutil.c.
- */
-
-#include "ccache.h"
-#include "hashutil.h"
-#include "test/framework.h"
-#include "test/util.h"
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// This file contains tests for functions in hashutil.c.
+
+#include "../ccache.h"
+#include "../hashutil.h"
+#include "framework.h"
+#include "util.h"
 
 TEST_SUITE(hashutil)
 
@@ -72,10 +68,16 @@ TEST(hash_command_output_stdout_versus_stderr)
        struct mdfour h1, h2;
        hash_start(&h1);
        hash_start(&h2);
+#ifndef _WIN32
        create_file("stderr.sh", "#!/bin/sh\necho foo >&2\n");
        chmod("stderr.sh", 0555);
        CHECK(hash_command_output(&h1, "echo foo", "not used"));
        CHECK(hash_command_output(&h2, "./stderr.sh", "not used"));
+#else
+       create_file("stderr.bat", "@echo off\r\necho foo>&2\r\n");
+       CHECK(hash_command_output(&h1, "echo foo", "not used"));
+       CHECK(hash_command_output(&h2, "stderr.bat", "not used"));
+#endif
        CHECK(hash_equal(&h1, &h2));
 }
 
@@ -84,10 +86,16 @@ TEST(hash_multicommand_output)
        struct mdfour h1, h2;
        hash_start(&h1);
        hash_start(&h2);
+#ifndef _WIN32
        create_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n");
        chmod("foo.sh", 0555);
        CHECK(hash_multicommand_output(&h2, "echo foo; echo bar", "not used"));
        CHECK(hash_multicommand_output(&h1, "./foo.sh", "not used"));
+#else
+       create_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n");
+       CHECK(hash_multicommand_output(&h2, "echo foo; echo bar", "not used"));
+       CHECK(hash_multicommand_output(&h1, "foo.bat", "not used"));
+#endif
        CHECK(hash_equal(&h1, &h2));
 }
 
index 9af995f..cb76176 100644 (file)
@@ -1,28 +1,24 @@
-/*
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-/*
- * This file contains tests for functions in lockfile.c.
- */
+// This file contains tests for functions in lockfile.c.
 
-#include "ccache.h"
-#include "test/framework.h"
-#include "test/util.h"
+#include "../ccache.h"
+#include "framework.h"
+#include "util.h"
 
 TEST_SUITE(lockfile)
 
index 79482d1..2815b9e 100644 (file)
@@ -1,29 +1,25 @@
-/*
- * Copyright (C) 2010-2011 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This file contains tests for statistics handling.
- */
-
-#include "ccache.h"
-#include "counters.h"
-#include "test/framework.h"
-#include "test/util.h"
+// Copyright (C) 2010-2011 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// This file contains tests for statistics handling.
+
+#include "../ccache.h"
+#include "../counters.h"
+#include "framework.h"
+#include "util.h"
 
 TEST_SUITE(stats)
 
index 3842fab..dca718c 100644 (file)
@@ -1,27 +1,23 @@
-/*
- * Copyright (C) 2010-2015 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * This file contains tests for functions in util.c.
- */
-
-#include "ccache.h"
-#include "test/framework.h"
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// This file contains tests for functions in util.c.
+
+#include "../ccache.h"
+#include "framework.h"
 
 TEST_SUITE(util)
 
@@ -180,7 +176,7 @@ TEST(parse_size_with_suffix)
        size_t i;
        struct { const char *size; int64_t expected; } sizes[] = {
                {"0", 0},
-               {"42", (int64_t)42 * 1000 * 1000 * 1000}, /* Default suffix: G */
+               {"42", (int64_t)42 * 1000 * 1000 * 1000}, // Default suffix: G
 
                {"78k",       78 * 1000},
                {"78K",       78 * 1000},
index adfecaf..826eebd 100644 (file)
@@ -1,23 +1,21 @@
-/*
- * Copyright (C) 2010 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2010-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-#include "system.h"
-#include "test/util.h"
+#include "../system.h"
+#include "util.h"
 
 #ifdef _WIN32
 #    define lstat(a, b) stat(a, b)
@@ -30,18 +28,6 @@ path_exists(const char *path)
        return lstat(path, &st) == 0;
 }
 
-bool
-is_symlink(const char *path)
-{
-#ifdef _WIN32
-       (void) path;
-       return 0;
-#else
-       struct stat st;
-       return lstat(path, &st) == 0 && S_ISLNK(st.st_mode);
-#endif
-}
-
 void
 create_file(const char *path, const char *content)
 {
index dd942d4..23636b9 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef TEST_UTIL_H
 #define TEST_UTIL_H
 
+#include <stdbool.h>
+
 bool path_exists(const char *path);
 bool is_symlink(const char *path);
 void create_file(const char *path, const char *content);
diff --git a/unify.c b/unify.c
index 6f19ea8..7c54e9f 100644 (file)
--- a/unify.c
+++ b/unify.c
@@ -1,34 +1,31 @@
-/*
- * Copyright (C) 2002 Andrew Tridgell
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * C/C++ unifier
- *
- * The idea is that changes that don't affect the resulting C code should not
- * change the hash. This is achieved by folding white-space and other
- * non-semantic fluff in the input into a single unified format.
- *
- * This unifier was design to match the output of the unifier in compilercache,
- * which is flex based. The major difference is that this unifier is much
- * faster (about 2x) and more forgiving of syntactic errors. Continuing on
- * syntactic errors is important to cope with C/C++ extensions in the local
- * compiler (for example, inline assembly systems).
- */
+// Copyright (C) 2002 Andrew Tridgell
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// C/C++ unifier
+//
+// The idea is that changes that don't affect the resulting C code should not
+// change the hash. This is achieved by folding white-space and other
+// non-semantic fluff in the input into a single unified format.
+//
+// This unifier was design to match the output of the unifier in compilercache,
+// which is flex based. The major difference is that this unifier is much
+// faster (about 2x) and more forgiving of syntactic errors. Continuing on
+// syntactic errors is important to cope with C/C++ extensions in the local
+// compiler (for example, inline assembly systems).
 
 #include "ccache.h"
 
@@ -56,21 +53,18 @@ static struct {
        const char *toks[7];
 } tokens[256];
 
-/* build up the table used by the unifier */
+// Build up the table used by the unifier.
 static void
 build_table(void)
 {
-       unsigned char c;
-       int i;
        static bool done;
-
        if (done) {
                return;
        }
        done = true;
 
        memset(tokens, 0, sizeof(tokens));
-       for (c = 0; c < 128; c++) {
+       for (unsigned char c = 0; c < 128; c++) {
                if (isalpha(c) || c == '_') {
                        tokens[c].type |= C_ALPHA;
                }
@@ -96,15 +90,15 @@ build_table(void)
        tokens['-'].type |= C_SIGN;
        tokens['+'].type |= C_SIGN;
 
-       for (i = 0; s_tokens[i]; i++) {
-               c = s_tokens[i][0];
+       for (int i = 0; s_tokens[i]; i++) {
+               unsigned char c = s_tokens[i][0];
                tokens[c].type |= C_TOKEN;
                tokens[c].toks[tokens[c].num_toks] = s_tokens[i];
                tokens[c].num_toks++;
        }
 }
 
-/* buffer up characters before hashing them */
+// Buffer up characters before hashing them.
 static void
 pushchar(struct mdfour *hash, unsigned char c)
 {
@@ -127,17 +121,13 @@ pushchar(struct mdfour *hash, unsigned char c)
        }
 }
 
-/* hash some C/C++ code after unifying */
+// Hash some C/C++ code after unifying.
 static void
 unify(struct mdfour *hash, unsigned char *p, size_t size)
 {
-       size_t ofs;
-       unsigned char q;
-       int i;
-
        build_table();
 
-       for (ofs = 0; ofs < size; ) {
+       for (size_t ofs = 0; ofs < size; ) {
                if (p[ofs] == '#') {
                        if ((size-ofs) > 2 && p[ofs+1] == ' ' && isdigit(p[ofs+2])) {
                                do {
@@ -200,7 +190,7 @@ unify(struct mdfour *hash, unsigned char *p, size_t size)
                }
 
                if (tokens[p[ofs]].type & C_QUOTE) {
-                       q = p[ofs];
+                       unsigned char q = p[ofs];
                        pushchar(hash, p[ofs]);
                        do {
                                ofs++;
@@ -217,7 +207,8 @@ unify(struct mdfour *hash, unsigned char *p, size_t size)
                }
 
                if (tokens[p[ofs]].type & C_TOKEN) {
-                       q = p[ofs];
+                       unsigned char q = p[ofs];
+                       int i;
                        for (i = 0; i < tokens[q].num_toks; i++) {
                                unsigned char *s = (unsigned char *)tokens[q].toks[i];
                                int len = strlen((char *)s);
@@ -244,15 +235,13 @@ unify(struct mdfour *hash, unsigned char *p, size_t size)
 }
 
 
-/* hash a file that consists of preprocessor output, but remove any line
-   number information from the hash
- */
+// Hash a file that consists of preprocessor output, but remove any line number
+// information from the hash.
 int
 unify_hash(struct mdfour *hash, const char *fname)
 {
        char *data;
        size_t size;
-
        if (!read_file(fname, 0, &data, &size)) {
                stats_update(STATS_PREPROCESSOR);
                return -1;
diff --git a/util.c b/util.c
index ce1341c..f048d97 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,21 +1,19 @@
-/*
- * Copyright (C) 2002 Andrew Tridgell
- * Copyright (C) 2009-2016 Joel Rosdahl
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// Copyright (C) 2002 Andrew Tridgell
+// Copyright (C) 2009-2016 Joel Rosdahl
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 #include "ccache.h"
 
@@ -31,6 +29,8 @@
 #ifdef _WIN32
 #include <windows.h>
 #include <sys/locking.h>
+#include <psapi.h>
+#include <tchar.h>
 #endif
 
 static FILE *logfile;
@@ -99,23 +99,17 @@ path_max(const char *path)
        return MAXPATHLEN;
 #elif defined(_PC_PATH_MAX)
        long maxlen = pathconf(path, _PC_PATH_MAX);
-       if (maxlen >= 4096) {
-               return maxlen;
-       } else {
-               return 4096;
-       }
+       return maxlen >= 4096 ? maxlen : 4096;
 #endif
 }
 
-/*
- * Warn about failure writing to the log file and then exit.
- */
+// Warn about failure writing to the log file and then exit.
 static void
 warn_log_fail(void)
 {
        extern struct conf *conf;
 
-       /* Note: Can't call fatal() since that would lead to recursion. */
+       // Note: Can't call fatal() since that would lead to recursion.
        fprintf(stderr, "ccache: error: Failed to write to %s: %s\n",
                conf->log_file, strerror(errno));
        x_exit(EXIT_FAILURE);
@@ -124,22 +118,19 @@ warn_log_fail(void)
 static void
 vlog(const char *format, va_list ap, bool log_updated_time)
 {
-       int rc1, rc2;
        if (!init_log()) {
                return;
        }
 
        log_prefix(log_updated_time);
-       rc1 = vfprintf(logfile, format, ap);
-       rc2 = fprintf(logfile, "\n");
+       int rc1 = vfprintf(logfile, format, ap);
+       int rc2 = fprintf(logfile, "\n");
        if (rc1 < 0 || rc2 < 0) {
                warn_log_fail();
        }
 }
 
-/*
- * Write a message to the log file (adding a newline) and flush.
- */
+// Write a message to the log file (adding a newline) and flush.
 void
 cc_log(const char *format, ...)
 {
@@ -152,10 +143,8 @@ cc_log(const char *format, ...)
        }
 }
 
-/*
- * Write a message to the log file (adding a newline) without flushing and with
- * a reused timestamp.
- */
+// Write a message to the log file (adding a newline) without flushing and with
+// a reused timestamp.
 void
 cc_bulklog(const char *format, ...)
 {
@@ -165,13 +154,10 @@ cc_bulklog(const char *format, ...)
        va_end(ap);
 }
 
-/*
- * Log an executed command to the CCACHE_LOGFILE location.
- */
+// Log an executed command to the CCACHE_LOGFILE location.
 void
 cc_log_argv(const char *prefix, char **argv)
 {
-       int rc;
        if (!init_log()) {
                return;
        }
@@ -179,20 +165,19 @@ cc_log_argv(const char *prefix, char **argv)
        log_prefix(true);
        fputs(prefix, logfile);
        print_command(logfile, argv);
-       rc = fflush(logfile);
+       int rc = fflush(logfile);
        if (rc) {
                warn_log_fail();
        }
 }
 
-/* something went badly wrong! */
+// Something went badly wrong!
 void
 fatal(const char *format, ...)
 {
        va_list ap;
-       char msg[1000];
-
        va_start(ap, format);
+       char msg[1000];
        vsnprintf(msg, sizeof(msg), format, ap);
        va_end(ap);
 
@@ -202,22 +187,17 @@ fatal(const char *format, ...)
        x_exit(1);
 }
 
-/*
- * Copy all data from fd_in to fd_out, decompressing data from fd_in if needed.
- */
+// Copy all data from fd_in to fd_out, decompressing data from fd_in if needed.
 void
 copy_fd(int fd_in, int fd_out)
 {
-       char buf[10240];
-       int n;
-       gzFile gz_in;
-
-       gz_in = gzdopen(dup(fd_in), "rb");
-
+       gzFile gz_in = gzdopen(dup(fd_in), "rb");
        if (!gz_in) {
                fatal("Failed to copy fd");
        }
 
+       int n;
+       char buf[READ_BUFFER_SIZE];
        while ((n = gzread(gz_in, buf, sizeof(buf))) > 0) {
                ssize_t written = 0;
                do {
@@ -236,7 +216,7 @@ copy_fd(int fd_in, int fd_out)
 }
 
 #ifndef HAVE_MKSTEMP
-/* cheap and nasty mkstemp replacement */
+// Cheap and nasty mkstemp replacement.
 int
 mkstemp(char *template)
 {
@@ -260,31 +240,25 @@ get_umask(void)
 }
 #endif
 
-/*
- * Copy src to dest, decompressing src if needed. compress_level > 0 decides
- * whether dest will be compressed, and with which compression level. Returns 0
- * on success and -1 on failure. On failure, errno represents the error.
- */
+// Copy src to dest, decompressing src if needed. compress_level > 0 decides
+// whether dest will be compressed, and with which compression level. Returns 0
+// on success and -1 on failure. On failure, errno represents the error.
 int
 copy_file(const char *src, const char *dest, int compress_level)
 {
-       int fd_in, fd_out;
-       gzFile gz_in = NULL, gz_out = NULL;
-       char buf[10240];
-       int n, written;
-       char *tmp_name;
-       struct stat st;
-       int errnum;
+       int fd_out;
+       gzFile gz_in = NULL;
+       gzFile gz_out = NULL;
        int saved_errno = 0;
 
-       /* open destination file */
-       tmp_name = x_strdup(dest);
+       // Open destination file.
+       char *tmp_name = x_strdup(dest);
        fd_out = create_tmp_fd(&tmp_name);
        cc_log("Copying %s to %s via %s (%scompressed)",
               src, dest, tmp_name, compress_level > 0 ? "" : "un");
 
-       /* open source file */
-       fd_in = open(src, O_RDONLY | O_BINARY);
+       // Open source file.
+       int fd_in = open(src, O_RDONLY | O_BINARY);
        if (fd_in == -1) {
                saved_errno = errno;
                cc_log("open error: %s", strerror(saved_errno));
@@ -300,11 +274,10 @@ copy_file(const char *src, const char *dest, int compress_level)
        }
 
        if (compress_level > 0) {
-               /*
-                * A gzip file occupies at least 20 bytes, so it will always
-                * occupy an entire filesystem block, even for empty files.
-                * Turn off compression for empty files to save some space.
-                */
+               // A gzip file occupies at least 20 bytes, so it will always occupy an
+               // entire filesystem block, even for empty files. Turn off compression for
+               // empty files to save some space.
+               struct stat st;
                if (x_fstat(fd_in, &st) != 0) {
                        goto error;
                }
@@ -323,7 +296,10 @@ copy_file(const char *src, const char *dest, int compress_level)
                gzsetparams(gz_out, compress_level, Z_DEFAULT_STRATEGY);
        }
 
+       int n;
+       char buf[READ_BUFFER_SIZE];
        while ((n = gzread(gz_in, buf, sizeof(buf))) > 0) {
+               int written;
                if (compress_level > 0) {
                        written = gzwrite(gz_out, buf, n);
                } else {
@@ -339,6 +315,7 @@ copy_file(const char *src, const char *dest, int compress_level)
                }
                if (written != n) {
                        if (compress_level > 0) {
+                               int errnum;
                                cc_log("gzwrite error: %s (errno: %s)",
                                       gzerror(gz_in, &errnum),
                                       strerror(saved_errno));
@@ -349,10 +326,9 @@ copy_file(const char *src, const char *dest, int compress_level)
                }
        }
 
-       /*
-        * gzeof won't tell if there's an error in the trailing CRC, so we must check
-        * gzerror before considering everything OK.
-        */
+       // gzeof won't tell if there's an error in the trailing CRC, so we must check
+       // gzerror before considering everything OK.
+       int errnum;
        gzerror(gz_in, &errnum);
        if (!gzeof(gz_in) || (errnum != Z_OK && errnum != Z_STREAM_END)) {
                saved_errno = errno;
@@ -379,7 +355,7 @@ copy_file(const char *src, const char *dest, int compress_level)
        fchmod(fd_out, 0666 & ~get_umask());
 #endif
 
-       /* the close can fail on NFS if out of space */
+       // The close can fail on NFS if out of space.
        if (close(fd_out) == -1) {
                saved_errno = errno;
                cc_log("close error: %s", strerror(saved_errno));
@@ -412,23 +388,19 @@ error:
        return -1;
 }
 
-/* Run copy_file() and, if successful, delete the source file. */
+// Run copy_file() and, if successful, delete the source file.
 int
 move_file(const char *src, const char *dest, int compress_level)
 {
-       int ret;
-
-       ret = copy_file(src, dest, compress_level);
+       int ret = copy_file(src, dest, compress_level);
        if (ret != -1) {
                x_unlink(src);
        }
        return ret;
 }
 
-/*
- * Like move_file(), but assumes that src is uncompressed and that src and dest
- * are on the same file system.
- */
+// Like move_file(), but assumes that src is uncompressed and that src and dest
+// are on the same file system.
 int
 move_uncompressed_file(const char *src, const char *dest, int compress_level)
 {
@@ -439,19 +411,16 @@ move_uncompressed_file(const char *src, const char *dest, int compress_level)
        }
 }
 
-/* test if a file is zlib compressed */
+// Test if a file is zlib compressed.
 bool
 file_is_compressed(const char *filename)
 {
-       FILE *f;
-
-       f = fopen(filename, "rb");
+       FILE *f = fopen(filename, "rb");
        if (!f) {
                return false;
        }
 
-       /* test if file starts with 1F8B, which is zlib's
-        * magic number */
+       // Test if file starts with 1F8B, which is zlib's magic number.
        if ((fgetc(f) != 0x1f) || (fgetc(f) != 0x8b)) {
                fclose(f);
                return false;
@@ -461,7 +430,7 @@ file_is_compressed(const char *filename)
        return true;
 }
 
-/* make sure a directory exists */
+// Make sure a directory exists.
 int
 create_dir(const char *dir)
 {
@@ -479,14 +448,13 @@ create_dir(const char *dir)
        return 0;
 }
 
-/* Create directories leading to path. Returns 0 on success, otherwise -1. */
+// Create directories leading to path. Returns 0 on success, otherwise -1.
 int
 create_parent_dirs(const char *path)
 {
-       struct stat st;
        int res;
        char *parent = dirname(path);
-
+       struct stat st;
        if (stat(parent, &st) == 0) {
                if (S_ISDIR(st.st_mode)) {
                        res = 0;
@@ -498,15 +466,14 @@ create_parent_dirs(const char *path)
                res = create_parent_dirs(parent);
                if (res == 0) {
                        res = mkdir(parent, 0777);
-                       /* Have to handle the condition of the directory already existing because
-                        * the file system could have changed in between calling stat and
-                        * actually creating the directory. This can happen when there are
-                        * multiple instances of ccache running and trying to create the same
-                        * directory chain, which usually is the case when the cache root does
-                        * not initially exist. As long as one of the processes creates the
-                        * directories then our condition is satisfied and we avoid a race
-                        * condition.
-                        */
+                       // Have to handle the condition of the directory already existing because
+                       // the file system could have changed in between calling stat and
+                       // actually creating the directory. This can happen when there are
+                       // multiple instances of ccache running and trying to create the same
+                       // directory chain, which usually is the case when the cache root does
+                       // not initially exist. As long as one of the processes creates the
+                       // directories then our condition is satisfied and we avoid a race
+                       // condition.
                        if (res != 0 && errno == EEXIST) {
                                res = 0;
                        }
@@ -518,9 +485,7 @@ create_parent_dirs(const char *path)
        return res;
 }
 
-/*
- * Return a static string with the current hostname.
- */
+// Return a static string with the current hostname.
 const char *
 get_hostname(void)
 {
@@ -540,21 +505,17 @@ get_hostname(void)
                return hostname;
        }
 
-       WORD wVersionRequested;
-       WSADATA wsaData;
-       int err;
-
-       wVersionRequested = MAKEWORD(2, 2);
-
-       err = WSAStartup(wVersionRequested, &wsaData);
+       WORD w_version_requested = MAKEWORD(2, 2);
+       WSADATA wsa_data;
+       int err = WSAStartup(w_version_requested, &wsa_data);
        if (err != 0) {
-               /* Tell the user that we could not find a usable Winsock DLL. */
+               // Tell the user that we could not find a usable Winsock DLL.
                cc_log("WSAStartup failed with error: %d", err);
                return hostname;
        }
 
-       if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
-               /* Tell the user that we could not find a usable WinSock DLL. */
+       if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) {
+               // Tell the user that we could not find a usable WinSock DLL.
                cc_log("Could not find a usable version of Winsock.dll");
                WSACleanup();
                return hostname;
@@ -562,31 +523,29 @@ get_hostname(void)
 
        int result = gethostname(hostname, sizeof(hostname) - 1);
        if (result != 0) {
-               int last_error = WSAGetLastError();
-               LPVOID lpMsgBuf;
-               LPVOID lpDisplayBuf;
-               DWORD dw = last_error;
+               LPVOID lp_msg_buf;
+               DWORD dw = WSAGetLastError();
 
                FormatMessage(
                  FORMAT_MESSAGE_ALLOCATE_BUFFER |
                  FORMAT_MESSAGE_FROM_SYSTEM |
                  FORMAT_MESSAGE_IGNORE_INSERTS,
                  NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                 (LPTSTR) &lpMsgBuf, 0, NULL);
+                 (LPTSTR) &lp_msg_buf, 0, NULL);
 
-               lpDisplayBuf = (LPVOID) LocalAlloc(
+               LPVOID lp_display_buf = (LPVOID) LocalAlloc(
                  LMEM_ZEROINIT,
-                 (lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) __FILE__) + 200)
+                 (lstrlen((LPCTSTR) lp_msg_buf) + lstrlen((LPCTSTR) __FILE__) + 200)
                  * sizeof(TCHAR));
-               _snprintf((LPTSTR) lpDisplayBuf,
-                         LocalSize(lpDisplayBuf) / sizeof(TCHAR),
+               _snprintf((LPTSTR) lp_display_buf,
+                         LocalSize(lp_display_buf) / sizeof(TCHAR),
                          TEXT("%s failed with error %d: %s"), __FILE__, dw,
-                         lpMsgBuf);
+                         lp_msg_buf);
 
-               cc_log("can't get hostname OS returned error: %s", (char *)lpDisplayBuf);
+               cc_log("can't get hostname OS returned error: %s", (char *)lp_display_buf);
 
-               LocalFree(lpMsgBuf);
-               LocalFree(lpDisplayBuf);
+               LocalFree(lp_msg_buf);
+               LocalFree(lp_display_buf);
        }
        WSACleanup();
 #endif
@@ -595,40 +554,31 @@ get_hostname(void)
        return hostname;
 }
 
-/*
- * Return a string to be passed to mkstemp to create a temporary file. Also
- * tries to cope with NFS by adding the local hostname.
- */
+// Return a string to be passed to mkstemp to create a temporary file. Also
+// tries to cope with NFS by adding the local hostname.
 const char *
 tmp_string(void)
 {
        static char *ret;
-
        if (!ret) {
                ret = format("%s.%u.XXXXXX", get_hostname(), (unsigned)getpid());
        }
-
        return ret;
 }
 
-/*
- * Return the hash result as a hex string. Size -1 means don't include size
- * suffix. Caller frees.
- */
+// Return the hash result as a hex string. Size -1 means don't include size
+// suffix. Caller frees.
 char *
 format_hash_as_string(const unsigned char *hash, int size)
 {
-       char *ret;
        int i;
-
-       ret = x_malloc(53);
+       char *ret = x_malloc(53);
        for (i = 0; i < 16; i++) {
                sprintf(&ret[i*2], "%02x", (unsigned) hash[i]);
        }
        if (size >= 0) {
                sprintf(&ret[i*2], "-%d", size);
        }
-
        return ret;
 }
 
@@ -636,14 +586,13 @@ char const CACHEDIR_TAG[] =
   "Signature: 8a477f597d28d172789f06886806bc55\n"
   "# This file is a cache directory tag created by ccache.\n"
   "# For information about cache directory tags, see:\n"
-  "#   http://www.brynosaurus.com/cachedir/\n";
+  "#\thttp://www.brynosaurus.com/cachedir/\n";
 
 int
 create_cachedirtag(const char *dir)
 {
-       struct stat st;
-       FILE *f;
        char *filename = format("%s/CACHEDIR.TAG", dir);
+       struct stat st;
        if (stat(filename, &st) == 0) {
                if (S_ISREG(st.st_mode)) {
                        goto success;
@@ -651,7 +600,7 @@ create_cachedirtag(const char *dir)
                errno = EEXIST;
                goto error;
        }
-       f = fopen(filename, "w");
+       FILE *f = fopen(filename, "w");
        if (!f) {
                goto error;
        }
@@ -670,14 +619,14 @@ error:
        return 1;
 }
 
-/* Construct a string according to a format. Caller frees. */
+// Construct a string according to a format. Caller frees.
 char *
 format(const char *format, ...)
 {
        va_list ap;
-       char *ptr = NULL;
-
        va_start(ap, format);
+
+       char *ptr = NULL;
        if (vasprintf(&ptr, format, ap) == -1) {
                fatal("Out of memory in format");
        }
@@ -689,39 +638,36 @@ format(const char *format, ...)
        return ptr;
 }
 
-/* This is like strdup() but dies if the malloc fails. */
+// This is like strdup() but dies if the malloc fails.
 char *
 x_strdup(const char *s)
 {
-       char *ret;
-       ret = strdup(s);
+       char *ret = strdup(s);
        if (!ret) {
                fatal("Out of memory in x_strdup");
        }
        return ret;
 }
 
-/* This is like strndup() but dies if the malloc fails. */
+// This is like strndup() but dies if the malloc fails.
 char *
 x_strndup(const char *s, size_t n)
 {
-       char *ret;
 #ifndef HAVE_STRNDUP
-       size_t m;
-
-       if (!s)
+       if (!s) {
                return NULL;
-       m = 0;
+       }
+       size_t m = 0;
        while (m < n && s[m]) {
                m++;
        }
-       ret = malloc(m + 1);
+       char *ret = malloc(m + 1);
        if (ret) {
                memcpy(ret, s, m);
                ret[m] = '\0';
        }
 #else
-       ret = strndup(s, n);
+       char *ret = strndup(s, n);
 #endif
        if (!ret) {
                fatal("x_strndup: Could not allocate %lu bytes", (unsigned long)n);
@@ -729,70 +675,63 @@ x_strndup(const char *s, size_t n)
        return ret;
 }
 
-/* This is like malloc() but dies if the malloc fails. */
+// This is like malloc() but dies if the malloc fails.
 void *
 x_malloc(size_t size)
 {
-       void *ret;
        if (size == 0) {
-               /*
-                * malloc() may return NULL if size is zero, so always do this to make sure
-                * that the code handles it regardless of platform.
-                */
+               // malloc() may return NULL if size is zero, so always do this to make sure
+               // that the code handles it regardless of platform.
                return NULL;
        }
-       ret = malloc(size);
+       void *ret = malloc(size);
        if (!ret) {
                fatal("x_malloc: Could not allocate %lu bytes", (unsigned long)size);
        }
        return ret;
 }
 
-/* This is like calloc() but dies if the allocation fails. */
+// This is like calloc() but dies if the allocation fails.
 void *
 x_calloc(size_t nmemb, size_t size)
 {
-       void *ret;
        if (nmemb * size == 0) {
-               /*
-                * calloc() may return NULL if nmemb or size is 0, so always do this to
-                * make sure that the code handles it regardless of platform.
-                */
+               // calloc() may return NULL if nmemb or size is 0, so always do this to
+               // make sure that the code handles it regardless of platform.
                return NULL;
        }
-       ret = calloc(nmemb, size);
+       void *ret = calloc(nmemb, size);
        if (!ret) {
                fatal("x_calloc: Could not allocate %lu bytes", (unsigned long)size);
        }
        return ret;
 }
 
-/* This is like realloc() but dies if the malloc fails. */
+// This is like realloc() but dies if the malloc fails.
 void *
 x_realloc(void *ptr, size_t size)
 {
-       void *p2;
        if (!ptr) {
                return x_malloc(size);
        }
-       p2 = realloc(ptr, size);
+       void *p2 = realloc(ptr, size);
        if (!p2) {
                fatal("x_realloc: Could not allocate %lu bytes", (unsigned long)size);
        }
        return p2;
 }
 
-/* This is like unsetenv. */
+// This is like unsetenv.
 void x_unsetenv(const char *name)
 {
 #ifdef HAVE_UNSETENV
        unsetenv(name);
 #else
-       putenv(x_strdup(name)); /* Leak to environment. */
+       putenv(x_strdup(name)); // Leak to environment.
 #endif
 }
 
-/* Like fstat() but also call cc_log on failure. */
+// Like fstat() but also call cc_log on failure.
 int
 x_fstat(int fd, struct stat *buf)
 {
@@ -803,7 +742,7 @@ x_fstat(int fd, struct stat *buf)
        return result;
 }
 
-/* Like lstat() but also call cc_log on failure. */
+// Like lstat() but also call cc_log on failure.
 int
 x_lstat(const char *pathname, struct stat *buf)
 {
@@ -814,7 +753,7 @@ x_lstat(const char *pathname, struct stat *buf)
        return result;
 }
 
-/* Like stat() but also call cc_log on failure. */
+// Like stat() but also call cc_log on failure.
 int
 x_stat(const char *pathname, struct stat *buf)
 {
@@ -825,17 +764,15 @@ x_stat(const char *pathname, struct stat *buf)
        return result;
 }
 
-/*
- * Construct a string according to the format and store it in *ptr. The
- * original *ptr is then freed.
- */
+// Construct a string according to the format and store it in *ptr. The
+// original *ptr is then freed.
 void
 reformat(char **ptr, const char *format, ...)
 {
        char *saved = *ptr;
-       va_list ap;
-
        *ptr = NULL;
+
+       va_list ap;
        va_start(ap, format);
        if (vasprintf(ptr, format, ap) == -1) {
                fatal("Out of memory in reformat");
@@ -850,24 +787,17 @@ reformat(char **ptr, const char *format, ...)
        }
 }
 
-/*
- * Recursive directory traversal. fn() is called on all entries in the tree.
- */
+// Recursive directory traversal. fn() is called on all entries in the tree.
 void
 traverse(const char *dir, void (*fn)(const char *, struct stat *))
 {
-       DIR *d;
-       struct dirent *de;
-
-       d = opendir(dir);
+       DIR *d = opendir(dir);
        if (!d) {
                return;
        }
 
+       struct dirent *de;
        while ((de = readdir(d))) {
-               char *fname;
-               struct stat st;
-
                if (str_eq(de->d_name, ".")) {
                        continue;
                }
@@ -879,7 +809,8 @@ traverse(const char *dir, void (*fn)(const char *, struct stat *))
                        continue;
                }
 
-               fname = format("%s/%s", dir, de->d_name);
+               char *fname = format("%s/%s", dir, de->d_name);
+               struct stat st;
                if (lstat(fname, &st)) {
                        if (errno != ENOENT && errno != ESTALE) {
                                fatal("lstat %s failed: %s", fname, strerror(errno));
@@ -900,12 +831,11 @@ traverse(const char *dir, void (*fn)(const char *, struct stat *))
 }
 
 
-/* return the base name of a file - caller frees */
+// Return the base name of a file - caller frees.
 char *
 basename(const char *path)
 {
-       char *p;
-       p = strrchr(path, '/');
+       char *p = strrchr(path, '/');
        if (p) {
                path = p + 1;
        }
@@ -919,19 +849,14 @@ basename(const char *path)
        return x_strdup(path);
 }
 
-/* return the dir name of a file - caller frees */
+// Return the dir name of a file - caller frees.
 char *
 dirname(const char *path)
 {
-       char *p;
+       char *s = x_strdup(path);
+       char *p = strrchr(s, '/');
 #ifdef _WIN32
-       char *p2;
-#endif
-       char *s;
-       s = x_strdup(path);
-       p = strrchr(s, '/');
-#ifdef _WIN32
-       p2 = strrchr(s, '\\');
+       char *p2 = strrchr(s, '\\');
        if (!p || (p2 && p < p2)) {
                p = p2;
        }
@@ -947,18 +872,14 @@ dirname(const char *path)
        return s;
 }
 
-/*
- * Return the file extension (including the dot) of a path as a pointer into
- * path. If path has no file extension, the empty string and the end of path is
- * returned.
- */
+// Return the file extension (including the dot) of a path as a pointer into
+// path. If path has no file extension, the empty string and the end of path is
+// returned.
 const char *
 get_extension(const char *path)
 {
        size_t len = strlen(path);
-       const char *p;
-
-       for (p = &path[len - 1]; p >= path; --p) {
+       for (const char *p = &path[len - 1]; p >= path; --p) {
                if (*p == '.') {
                        return p;
                }
@@ -969,17 +890,15 @@ get_extension(const char *path)
        return &path[len];
 }
 
-/*
- * Return a string containing the given path without the filename extension.
- * Caller frees.
- */
+// Return a string containing the given path without the filename extension.
+// Caller frees.
 char *
 remove_extension(const char *path)
 {
        return x_strndup(path, strlen(path) - strlen(get_extension(path)));
 }
 
-/* return size on disk of a file */
+// Return size on disk of a file.
 size_t
 file_size(struct stat *st)
 {
@@ -988,14 +907,14 @@ file_size(struct stat *st)
 #else
        size_t size = st->st_blocks * 512;
        if ((size_t)st->st_size > size) {
-               /* probably a broken stat() call ... */
+               // Probably a broken stat() call...
                size = (st->st_size + 1023) & ~1023;
        }
        return size;
 #endif
 }
 
-/* Format a size as a human-readable string. Caller frees. */
+// Format a size as a human-readable string. Caller frees.
 char *
 format_human_readable_size(uint64_t v)
 {
@@ -1010,7 +929,7 @@ format_human_readable_size(uint64_t v)
        return s;
 }
 
-/* Format a size as a parsable string. Caller frees. */
+// Format a size as a parsable string. Caller frees.
 char *
 format_parsable_size_with_suffix(uint64_t size)
 {
@@ -1027,19 +946,16 @@ format_parsable_size_with_suffix(uint64_t size)
        return s;
 }
 
-/*
- * Parse a "size value", i.e. a string that can end in k, M, G, T (10-based
- * suffixes) or Ki, Mi, Gi, Ti (2-based suffixes). For backward compatibility,
- * K is also recognized as a synonym of k.
- */
+// Parse a "size value", i.e. a string that can end in k, M, G, T (10-based
+// suffixes) or Ki, Mi, Gi, Ti (2-based suffixes). For backward compatibility,
+// K is also recognized as a synonym of k.
 bool
 parse_size_with_suffix(const char *str, uint64_t *size)
 {
-       char *p;
-       double x;
-
        errno = 0;
-       x = strtod(str, &p);
+
+       char *p;
+       double x = strtod(str, &p);
        if (errno != 0 || x < 0 || p == str || *str == '\0') {
                return false;
        }
@@ -1049,12 +965,7 @@ parse_size_with_suffix(const char *str, uint64_t *size)
        }
 
        if (*p != '\0') {
-               unsigned multiplier;
-               if (*(p+1) == 'i') {
-                       multiplier = 1024;
-               } else {
-                       multiplier = 1000;
-               }
+               unsigned multiplier = *(p+1) == 'i' ? 1024 : 1000;
                switch (*p) {
                case 'T':
                        x *= multiplier;
@@ -1070,7 +981,7 @@ parse_size_with_suffix(const char *str, uint64_t *size)
                        return false;
                }
        } else {
-               /* Default suffix: G. */
+               // Default suffix: G.
                x *= 1000 * 1000 * 1000;
        }
        *size = x;
@@ -1078,36 +989,119 @@ parse_size_with_suffix(const char *str, uint64_t *size)
 }
 
 
-/* A sane realpath() function, trying to cope with stupid path limits and a
- * broken API. */
+#if !defined(HAVE_REALPATH) && \
+    defined(_WIN32) && \
+    !defined(HAVE_GETFINALPATHNAMEBYHANDLEW)
+static BOOL GetFileNameFromHandle(HANDLE file_handle, TCHAR *filename,
+                                  WORD cch_filename)
+{
+       BOOL success = FALSE;
+
+       // Get the file size.
+       DWORD file_size_hi = 0;
+       DWORD file_size_lo = GetFileSize(file_handle, &file_size_hi);
+       if (file_size_lo == 0 && file_size_hi == 0) {
+               // Cannot map a file with a length of zero.
+               return FALSE;
+       }
+
+       // Create a file mapping object.
+       HANDLE file_map =
+         CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 1, NULL);
+       if (!file_map) {
+               return FALSE;
+       }
+
+       // Create a file mapping to get the file name.
+       void *mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1);
+       if (mem) {
+               if (GetMappedFileName(GetCurrentProcess(),
+                                     mem,
+                                     filename,
+                                     cch_filename)) {
+                       // Translate path with device name to drive letters.
+                       TCHAR temp[512];
+                       temp[0] = '\0';
+
+                       if (GetLogicalDriveStrings(512-1, temp)) {
+                               TCHAR name[MAX_PATH];
+                               TCHAR drive[3] = TEXT(" :");
+                               BOOL found = FALSE;
+                               TCHAR *p = temp;
+
+                               do {
+                                       // Copy the drive letter to the template string.
+                                       *drive = *p;
+
+                                       // Look up each device name.
+                                       if (QueryDosDevice(drive, name, MAX_PATH)) {
+                                               size_t name_len = _tcslen(name);
+                                               if (name_len < MAX_PATH) {
+                                                       found = _tcsnicmp(filename, name, name_len) == 0
+                                                               && *(filename + name_len) == _T('\\');
+                                                       if (found) {
+                                                               // Reconstruct filename using temp_file and replace device path
+                                                               // with DOS path.
+                                                               TCHAR temp_file[MAX_PATH];
+                                                               _sntprintf(temp_file,
+                                                                          MAX_PATH - 1,
+                                                                          TEXT("%s%s"),
+                                                                          drive,
+                                                                          filename+name_len);
+                                                               _tcsncpy(filename, temp_file, _tcslen(temp_file));
+                                                       }
+                                               }
+                                       }
+
+                                       // Go to the next NULL character.
+                                       while (*p++) {
+                                               // Do nothing.
+                                       }
+                               } while (!found && *p); // End of string.
+                       }
+               }
+               success = TRUE;
+               UnmapViewOfFile(mem);
+       }
+
+       CloseHandle(file_map);
+       return success;
+}
+#endif
+
+// A sane realpath() function, trying to cope with stupid path limits and a
+// broken API. Caller frees.
 char *
 x_realpath(const char *path)
 {
        long maxlen = path_max(path);
-       char *ret, *p;
-#if !defined(HAVE_REALPATH) && defined(_WIN32)
-       HANDLE path_handle;
-#endif
-
-       ret = x_malloc(maxlen);
+       char *ret = x_malloc(maxlen);
+       char *p;
 
 #if HAVE_REALPATH
        p = realpath(path, ret);
 #elif defined(_WIN32)
-       path_handle = CreateFile(
+       if (path[0] == '/') {
+               path++;  // Skip leading slash.
+       }
+       HANDLE path_handle = CreateFile(
          path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
          FILE_ATTRIBUTE_NORMAL, NULL);
        if (INVALID_HANDLE_VALUE != path_handle) {
+#ifdef HAVE_GETFINALPATHNAMEBYHANDLEW
                GetFinalPathNameByHandle(path_handle, ret, maxlen, FILE_NAME_NORMALIZED);
+#else
+               GetFileNameFromHandle(path_handle, ret, maxlen);
+#endif
                CloseHandle(path_handle);
-               p = ret + 4; /* strip \\?\ from the file name */
+               p = ret + 4; // Strip \\?\ from the file name.
        } else {
                snprintf(ret, maxlen, "%s", path);
                p = ret;
        }
 #else
-       /* yes, there are such systems. This replacement relies on
-          the fact that when we call x_realpath we only care about symlinks */
+       // Yes, there are such systems. This replacement relies on the fact that when
+       // we call x_realpath we only care about symlinks.
        {
                int len = readlink(path, ret, maxlen-1);
                if (len == -1) {
@@ -1127,7 +1121,7 @@ x_realpath(const char *path)
        return NULL;
 }
 
-/* a getcwd that will returns an allocated buffer */
+// A getcwd that will returns an allocated buffer.
 char *
 gnu_getcwd(void)
 {
@@ -1148,31 +1142,31 @@ gnu_getcwd(void)
 }
 
 #ifndef HAVE_STRTOK_R
-/* strtok_r replacement */
+// strtok_r replacement.
 char *
 strtok_r(char *str, const char *delim, char **saveptr)
 {
-       int len;
-       char *ret;
-       if (!str)
+       if (!str) {
                str = *saveptr;
-       len = strlen(str);
-       ret = strtok(str, delim);
+       }
+       int len = strlen(str);
+       char *ret = strtok(str, delim);
        if (ret) {
                char *save = ret;
-               while (*save++) ;
-               if ((len + 1) == (intptr_t) (save - str))
+               while (*save++) {
+                       // Do nothing.
+               }
+               if ((len + 1) == (intptr_t) (save - str)) {
                        save--;
+               }
                *saveptr = save;
        }
        return ret;
 }
 #endif
 
-/*
- * Create an empty temporary file. *fname will be reallocated and set to the
- * resulting filename. Returns an open file descriptor to the file.
- */
+// Create an empty temporary file. *fname will be reallocated and set to the
+// resulting filename. Returns an open file descriptor to the file.
 int
 create_tmp_fd(char **fname)
 {
@@ -1200,10 +1194,8 @@ create_tmp_fd(char **fname)
        return fd;
 }
 
-/*
- * Create an empty temporary file. *fname will be reallocated and set to the
- * resulting filename. Returns an open FILE*.
- */
+// Create an empty temporary file. *fname will be reallocated and set to the
+// resulting filename. Returns an open FILE*.
 FILE *
 create_tmp_file(char **fname, const char *mode)
 {
@@ -1214,9 +1206,7 @@ create_tmp_file(char **fname, const char *mode)
        return file;
 }
 
-/*
- * Return current user's home directory, or NULL if it can't be determined.
- */
+// Return current user's home directory, or NULL if it can't be determined.
 const char *
 get_home_directory(void)
 {
@@ -1241,23 +1231,19 @@ get_home_directory(void)
        return NULL;
 }
 
-/*
- * Get the current directory by reading $PWD. If $PWD isn't sane, gnu_getcwd()
- * is used. Caller frees.
- */
+// Get the current directory by reading $PWD. If $PWD isn't sane, gnu_getcwd()
+// is used. Caller frees.
 char *
 get_cwd(void)
 {
-       char *pwd;
-       char *cwd;
        struct stat st_pwd;
        struct stat st_cwd;
 
-       cwd = gnu_getcwd();
+       char *cwd = gnu_getcwd();
        if (!cwd) {
                return NULL;
        }
-       pwd = getenv("PWD");
+       char *pwd = getenv("PWD");
        if (!pwd) {
                return cwd;
        }
@@ -1275,9 +1261,7 @@ get_cwd(void)
        }
 }
 
-/*
- * Check whether s1 and s2 have the same executable name.
- */
+// Check whether s1 and s2 have the same executable name.
 bool
 same_executable_name(const char *s1, const char *s2)
 {
@@ -1294,10 +1278,8 @@ same_executable_name(const char *s1, const char *s2)
 #endif
 }
 
-/*
- * Compute the length of the longest directory path that is common to two
- * paths. s1 is assumed to be the path to a directory.
- */
+// Compute the length of the longest directory path that is common to two
+// paths. s1 is assumed to be the path to a directory.
 size_t
 common_dir_prefix_length(const char *s1, const char *s2)
 {
@@ -1313,22 +1295,19 @@ common_dir_prefix_length(const char *s1, const char *s2)
                p2--;
        }
        if (!*p1 && !*p2 && p2 == s2 + 1) {
-               /* Special case for s1 and s2 both being "/". */
+               // Special case for s1 and s2 both being "/".
                return 0;
        }
        return p1 - s1;
 }
 
-/*
- * Compute a relative path from from (an absolute path to a directory) to to (a
- * path). Assumes that both from and to are well-formed and canonical. Caller
- * frees.
- */
+// Compute a relative path from from (an absolute path to a directory) to to (a
+// path). Assumes that both from and to are well-formed and canonical. Caller
+// frees.
 char *
 get_relative_path(const char *from, const char *to)
 {
        size_t common_prefix_len;
-       int i;
        char *result;
 
        assert(from && is_absolute_path(from));
@@ -1339,8 +1318,15 @@ get_relative_path(const char *from, const char *to)
        }
 
 #ifdef _WIN32
-       // Both paths are absolute, drop the drive letters
-       assert(from[0] == to[0]); // Assume the same drive letter
+       // Paths can be escaped by a slash for use with -isystem.
+       if (from[0] == '/') {
+               from++;
+       }
+       if (to[0] == '/') {
+               to++;
+       }
+       // Both paths are absolute, drop the drive letters.
+       assert(from[0] == to[0]); // Assume the same drive letter.
        from += 2;
        to += 2;
 #endif
@@ -1358,10 +1344,8 @@ get_relative_path(const char *from, const char *to)
        if (strlen(to) > common_prefix_len) {
                reformat(&result, "%s%s", result, to + common_prefix_len + 1);
        }
-       i = strlen(result) - 1;
-       while (i >= 0 && result[i] == '/') {
+       for (int i = strlen(result) - 1; i >= 0 && result[i] == '/'; i--) {
                result[i] = '\0';
-               i--;
        }
        if (str_eq(result, "")) {
                free(result);
@@ -1370,9 +1354,7 @@ get_relative_path(const char *from, const char *to)
        return result;
 }
 
-/*
- * Return whether path is absolute.
- */
+// Return whether path is absolute.
 bool
 is_absolute_path(const char *path)
 {
@@ -1383,25 +1365,34 @@ is_absolute_path(const char *path)
 #endif
 }
 
-/*
- * Return whether the argument is a full path.
- */
+// Return whether the argument is a full path.
 bool
 is_full_path(const char *path)
 {
-       if (strchr(path, '/'))
+       if (strchr(path, '/')) {
                return true;
+       }
 #ifdef _WIN32
-       if (strchr(path, '\\'))
+       if (strchr(path, '\\')) {
                return true;
+       }
 #endif
        return false;
 }
 
-/*
- * Update the modification time of a file in the cache to save it from LRU
- * cleanup.
- */
+bool is_symlink(const char *path)
+{
+#ifdef _WIN32
+       (void)path;
+       return false;
+#else
+       struct stat st;
+       return x_lstat(path, &st) == 0 && ((st.st_mode & S_IFMT) == S_IFLNK);
+#endif
+}
+
+// Update the modification time of a file in the cache to save it from LRU
+// cleanup.
 void
 update_mtime(const char *path)
 {
@@ -1412,10 +1403,8 @@ update_mtime(const char *path)
 #endif
 }
 
-/*
- * If exit() already has been called, call _exit(), otherwise exit(). This is
- * used to avoid calling exit() inside an atexit handler.
- */
+// If exit() already has been called, call _exit(), otherwise exit(). This is
+// used to avoid calling exit() inside an atexit handler.
 void
 x_exit(int status)
 {
@@ -1428,43 +1417,41 @@ x_exit(int status)
        }
 }
 
-/*
- * Rename oldpath to newpath (deleting newpath).
- */
+// Rename oldpath to newpath (deleting newpath).
 int
 x_rename(const char *oldpath, const char *newpath)
 {
 #ifndef _WIN32
        return rename(oldpath, newpath);
 #else
-       /* Windows' rename() refuses to overwrite an existing file. */
-       unlink(newpath);  /* not x_unlink, as x_unlink calls x_rename */
-       /* If the function succeeds, the return value is nonzero. */
+       // Windows' rename() refuses to overwrite an existing file.
+       unlink(newpath); // Not x_unlink, as x_unlink calls x_rename.
+       // If the function succeeds, the return value is nonzero.
        if (MoveFileA(oldpath, newpath) == 0) {
-               LPVOID lpMsgBuf;
-               LPVOID lpDisplayBuf;
+               LPVOID lp_msg_buf;
                DWORD dw = GetLastError();
-
                FormatMessage(
                  FORMAT_MESSAGE_ALLOCATE_BUFFER |
                  FORMAT_MESSAGE_FROM_SYSTEM |
                  FORMAT_MESSAGE_IGNORE_INSERTS,
-                 NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,
-                 0, NULL);
+                 NULL, dw,
+                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lp_msg_buf,
+                 0,
+                 NULL);
 
-               lpDisplayBuf = (LPVOID) LocalAlloc(
+               LPVOID lp_display_buf = (LPVOID) LocalAlloc(
                  LMEM_ZEROINIT,
-                 (lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) __FILE__) + 40)
+                 (lstrlen((LPCTSTR) lp_msg_buf) + lstrlen((LPCTSTR) __FILE__) + 40)
                  * sizeof(TCHAR));
-               _snprintf((LPTSTR) lpDisplayBuf,
-                         LocalSize(lpDisplayBuf) / sizeof(TCHAR),
-                         TEXT("%s failed with error %d: %s"), __FILE__, dw, lpMsgBuf);
+               _snprintf((LPTSTR) lp_display_buf,
+                         LocalSize(lp_display_buf) / sizeof(TCHAR),
+                         TEXT("%s failed with error %d: %s"), __FILE__, dw, lp_msg_buf);
 
                cc_log("can't rename file %s to %s OS returned error: %s",
-                      oldpath, newpath, (char *) lpDisplayBuf);
+                      oldpath, newpath, (char *) lp_display_buf);
 
-               LocalFree(lpMsgBuf);
-               LocalFree(lpDisplayBuf);
+               LocalFree(lp_msg_buf);
+               LocalFree(lp_display_buf);
                return -1;
        } else {
                return 0;
@@ -1472,49 +1459,45 @@ x_rename(const char *oldpath, const char *newpath)
 #endif
 }
 
-/*
- * Remove path, NFS hazardous. Use only for temporary files that will not exist
- * on other systems. That is, the path should include tmp_string().
- */
+// Remove path, NFS hazardous. Use only for temporary files that will not exist
+// on other systems. That is, the path should include tmp_string().
 int
 tmp_unlink(const char *path)
 {
-       int rc;
        cc_log("Unlink %s", path);
-       rc = unlink(path);
+       int rc = unlink(path);
        if (rc) {
                cc_log("Unlink failed: %s", strerror(errno));
        }
        return rc;
 }
 
-/*
- * Remove path, NFS safe.
- */
+// Remove path, NFS safe.
 int
 x_unlink(const char *path)
 {
-       /*
-        * If path is on an NFS share, unlink isn't atomic, so we rename to a temp
-        * file. We don't care if the temp file is trashed, so it's always safe to
-        * unlink it first.
-        */
-       char *tmp_name = format("%s.rm.%s", path, tmp_string());
-       int result = 0;
        int saved_errno = 0;
+
+       // If path is on an NFS share, unlink isn't atomic, so we rename to a temp
+       // file. We don't care if the temp file is trashed, so it's always safe to
+       // unlink it first.
+       char *tmp_name = format("%s.rm.%s", path, tmp_string());
        cc_log("Unlink %s via %s", path, tmp_name);
+
+       int result = 0;
        if (x_rename(path, tmp_name) == -1) {
                result = -1;
                saved_errno = errno;
                goto out;
        }
        if (unlink(tmp_name) == -1) {
-               /* If it was released in a race, that's OK. */
+               // If it was released in a race, that's OK.
                if (errno != ENOENT && errno != ESTALE) {
                        result = -1;
                        saved_errno = errno;
                }
        }
+
 out:
        free(tmp_name);
        if (result) {
@@ -1525,16 +1508,13 @@ out:
 }
 
 #ifndef _WIN32
-/* Like readlink() but returns the string or NULL on failure. Caller frees. */
+// Like readlink() but returns the string or NULL on failure. Caller frees.
 char *
 x_readlink(const char *path)
 {
        long maxlen = path_max(path);
-       ssize_t len;
-       char *buf;
-
-       buf = x_malloc(maxlen);
-       len = readlink(path, buf, maxlen-1);
+       char *buf = x_malloc(maxlen);
+       ssize_t len = readlink(path, buf, maxlen-1);
        if (len == -1) {
                free(buf);
                return NULL;
@@ -1544,16 +1524,11 @@ x_readlink(const char *path)
 }
 #endif
 
-/*
- * Reads the content of a file. Size hint 0 means no hint. Returns true on
- * success, otherwise false.
- */
+// Reads the content of a file. Size hint 0 means no hint. Returns true on
+// success, otherwise false.
 bool
 read_file(const char *path, size_t size_hint, char **data, size_t *size)
 {
-       int fd, ret;
-       size_t pos = 0, allocated;
-
        if (size_hint == 0) {
                struct stat st;
                if (x_stat(path, &st) == 0) {
@@ -1562,12 +1537,14 @@ read_file(const char *path, size_t size_hint, char **data, size_t *size)
        }
        size_hint = (size_hint < 1024) ? 1024 : size_hint;
 
-       fd = open(path, O_RDONLY | O_BINARY);
+       int fd = open(path, O_RDONLY | O_BINARY);
        if (fd == -1) {
                return false;
        }
-       allocated = size_hint;
+       size_t allocated = size_hint;
        *data = x_malloc(allocated);
+       int ret;
+       size_t pos = 0;
        while (true) {
                if (pos > allocated / 2) {
                        allocated *= 2;
@@ -1593,16 +1570,13 @@ read_file(const char *path, size_t size_hint, char **data, size_t *size)
        return true;
 }
 
-/*
- * Return the content (with NUL termination) of a text file, or NULL on error.
- * Caller frees. Size hint 0 means no hint.
- */
+// Return the content (with NUL termination) of a text file, or NULL on error.
+// Caller frees. Size hint 0 means no hint.
 char *
 read_text_file(const char *path, size_t size_hint)
 {
        size_t size;
        char *data;
-
        if (read_file(path, size_hint, &data, &size)) {
                data = x_realloc(data, size + 1);
                data[size] = '\0';
@@ -1615,20 +1589,18 @@ read_text_file(const char *path, size_t size_hint)
 static bool
 expand_variable(const char **str, char **result, char **errmsg)
 {
-       bool curly;
-       const char *p, *q;
-       char *name;
-       const char *value;
-
        assert(**str == '$');
-       p = *str + 1;
+
+       bool curly;
+       const char *p = *str + 1;
        if (*p == '{') {
                curly = true;
                ++p;
        } else {
                curly = false;
        }
-       q = p;
+
+       const char *q = p;
        while (isalnum(*q) || *q == '_') {
                ++q;
        }
@@ -1640,13 +1612,13 @@ expand_variable(const char **str, char **result, char **errmsg)
        }
 
        if (q == p) {
-               /* Special case: don't consider a single $ the start of a variable. */
+               // Special case: don't consider a single $ the start of a variable.
                reformat(result, "%s$", *result);
                return true;
        }
 
-       name = x_strndup(p, q - p);
-       value = getenv(name);
+       char *name = x_strndup(p, q - p);
+       const char *value = getenv(name);
        if (!value) {
                *errmsg = format("environment variable \"%s\" not set", name);
                free(name);
@@ -1661,25 +1633,19 @@ expand_variable(const char **str, char **result, char **errmsg)
        return true;
 }
 
-/*
- * Substitute all instances of $VAR or ${VAR}, where VAR is an environment
- * variable, in a string. Caller frees. If one of the environment variables
- * doesn't exist, NULL will be returned and *errmsg will be an appropriate
- * error message (caller frees).
- */
+// Substitute all instances of $VAR or ${VAR}, where VAR is an environment
+// variable, in a string. Caller frees. If one of the environment variables
+// doesn't exist, NULL will be returned and *errmsg will be an appropriate
+// error message (caller frees).
 char *
 subst_env_in_string(const char *str, char **errmsg)
 {
-       const char *p; /* Interval start. */
-       const char *q; /* Interval end. */
-       char *result;
-
        assert(errmsg);
        *errmsg = NULL;
 
-       result = x_strdup("");
-       p = str;
-       q = str;
+       char *result = x_strdup("");
+       const char *p = str; // Interval start.
+       const char *q = str; // Interval end.
        for (q = str; *q; ++q) {
                if (*q == '$') {
                        reformat(&result, "%s%.*s", result, (int)(q - p), p);
index f959ae0..f3e0fa1 100644 (file)
--- a/version.c
+++ b/version.c
@@ -1 +1 @@
-const char CCACHE_VERSION[] = "3.2.9";
+const char CCACHE_VERSION[] = "3.3";
index 870f89b..4fd3f3c 100644 (file)
@@ -1504,9 +1504,10 @@ z_streamp strm;
 {
     struct inflate_state FAR *state;
 
-    if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16;
+    if (strm == Z_NULL || strm->state == Z_NULL)
+        return (long)(((unsigned long)0 - 1) << 16);
     state = (struct inflate_state FAR *)strm->state;
-    return ((long)(state->back) << 16) +
+    return (long)(((unsigned long)((long)state->back)) << 16) +
         (state->mode == COPY ? state->length :
             (state->mode == MATCH ? state->was - state->length : 0));
 }