Import ccache-2.4 source
git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@10900 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
d63f23bb84
commit
482d91951a
27 changed files with 11335 additions and 0 deletions
8
CCache/.cvsignore
Normal file
8
CCache/.cvsignore
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
Makefile
|
||||||
|
ccache
|
||||||
|
*gz
|
||||||
|
config.h
|
||||||
|
config.log
|
||||||
|
config.status
|
||||||
|
tca.log
|
||||||
|
tca.map
|
||||||
339
CCache/COPYING
Normal file
339
CCache/COPYING
Normal file
|
|
@ -0,0 +1,339 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
675 Mass Ave, Cambridge, MA 02139, USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Library General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Appendix: How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) 19yy <name of author>
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Library General
|
||||||
|
Public License instead of this License.
|
||||||
53
CCache/Makefile.in
Normal file
53
CCache/Makefile.in
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
srcdir=@srcdir@
|
||||||
|
VPATH=@srcdir@
|
||||||
|
|
||||||
|
prefix=@prefix@
|
||||||
|
exec_prefix=@exec_prefix@
|
||||||
|
bindir=@bindir@
|
||||||
|
mandir=@mandir@
|
||||||
|
INSTALLCMD=@INSTALL@
|
||||||
|
|
||||||
|
CC=@CC@
|
||||||
|
CFLAGS=@CFLAGS@ -I.
|
||||||
|
EXEEXT=@EXEEXT@
|
||||||
|
|
||||||
|
OBJS= ccache.o mdfour.o hash.o execute.o util.o args.o stats.o \
|
||||||
|
cleanup.o snprintf.o unify.o
|
||||||
|
HEADERS = ccache.h mdfour.h
|
||||||
|
|
||||||
|
all: ccache$(EXEEXT)
|
||||||
|
|
||||||
|
docs: ccache.1 web/ccache-man.html
|
||||||
|
|
||||||
|
ccache$(EXEEXT): $(OBJS) $(HEADERS)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $(OBJS)
|
||||||
|
|
||||||
|
ccache.1: ccache.yo
|
||||||
|
-yodl2man -o ccache.1 ccache.yo
|
||||||
|
|
||||||
|
web/ccache-man.html: ccache.yo
|
||||||
|
mkdir -p man
|
||||||
|
yodl2html -o web/ccache-man.html ccache.yo
|
||||||
|
|
||||||
|
install: ccache$(EXEEXT) ccache.1
|
||||||
|
${INSTALLCMD} -d $(DESTDIR)${bindir}
|
||||||
|
${INSTALLCMD} -m 755 ccache$(EXEEXT) $(DESTDIR)${bindir}
|
||||||
|
${INSTALLCMD} -d $(DESTDIR)${mandir}/man1
|
||||||
|
${INSTALLCMD} -m 644 ${srcdir}/ccache.1 $(DESTDIR)${mandir}/man1/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
/bin/rm -f $(OBJS) *~ ccache$(EXEEXT)
|
||||||
|
|
||||||
|
test: test.sh
|
||||||
|
CC='$(CC)' ./test.sh
|
||||||
|
|
||||||
|
check: test
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
/bin/rm -f Makefile config.h config.sub config.log build-stamp config.status
|
||||||
|
|
||||||
|
# FIXME: To fix this, test.sh needs to be able to take ccache from the
|
||||||
|
# installed prefix, not from the source dir.
|
||||||
|
installcheck:
|
||||||
|
@echo "WARNING! This is not really \"installcheck\" yet."
|
||||||
|
$(MAKE) check
|
||||||
31
CCache/README
Normal file
31
CCache/README
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
This is a re-implementation of "compilercache" in C
|
||||||
|
|
||||||
|
The original compilercache scripts were by Erik Thiele
|
||||||
|
(erikyyy@erikyyy.de) and I would like to thank him for an excellent
|
||||||
|
piece of work. See http://www.erikyyy.de/compilercache/ for the
|
||||||
|
original shell scripts.
|
||||||
|
|
||||||
|
I wrote ccache because I wanted to get a bit more speed out of a
|
||||||
|
compiler cache and I wanted to remove some of the limitations of the
|
||||||
|
shell-script version.
|
||||||
|
|
||||||
|
Please see the manual page and documentation at
|
||||||
|
http://ccache.samba.org/
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
------------
|
||||||
|
|
||||||
|
Please run:
|
||||||
|
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
then read the ccache manual page
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Andrew Tridgell
|
||||||
|
http://samba.org/~tridge/
|
||||||
|
bugs@ccache.samba.org
|
||||||
|
|
||||||
91
CCache/args.c
Normal file
91
CCache/args.c
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
convenient routines for argument list handling
|
||||||
|
|
||||||
|
Copyright (C) Andrew Tridgell 2002
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ccache.h"
|
||||||
|
|
||||||
|
ARGS *args_init(int init_argc, char **init_args)
|
||||||
|
{
|
||||||
|
ARGS *args;
|
||||||
|
int i;
|
||||||
|
args = (ARGS *)x_malloc(sizeof(ARGS));
|
||||||
|
args->argc = 0;
|
||||||
|
args->argv = (char **)x_malloc(sizeof(char *));
|
||||||
|
args->argv[0] = NULL;
|
||||||
|
for (i=0;i<init_argc;i++) {
|
||||||
|
args_add(args, init_args[i]);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void args_add(ARGS *args, const char *s)
|
||||||
|
{
|
||||||
|
args->argv = (char**)x_realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
||||||
|
args->argv[args->argc] = x_strdup(s);
|
||||||
|
args->argc++;
|
||||||
|
args->argv[args->argc] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pop the last element off the args list */
|
||||||
|
void args_pop(ARGS *args, int n)
|
||||||
|
{
|
||||||
|
while (n--) {
|
||||||
|
args->argc--;
|
||||||
|
free(args->argv[args->argc]);
|
||||||
|
args->argv[args->argc] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove the first element of the argument list */
|
||||||
|
void args_remove_first(ARGS *args)
|
||||||
|
{
|
||||||
|
free(args->argv[0]);
|
||||||
|
memmove(&args->argv[0],
|
||||||
|
&args->argv[1],
|
||||||
|
args->argc * sizeof(args->argv[0]));
|
||||||
|
args->argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add an argument into the front of the argument list */
|
||||||
|
void args_add_prefix(ARGS *args, const char *s)
|
||||||
|
{
|
||||||
|
args->argv = (char**)x_realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
||||||
|
memmove(&args->argv[1], &args->argv[0],
|
||||||
|
(args->argc+1) * sizeof(args->argv[0]));
|
||||||
|
args->argv[0] = x_strdup(s);
|
||||||
|
args->argc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* strip any arguments beginning with the specified prefix */
|
||||||
|
void args_strip(ARGS *args, const char *prefix)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i<args->argc; ) {
|
||||||
|
if (strncmp(args->argv[i], prefix, strlen(prefix)) == 0) {
|
||||||
|
free(args->argv[i]);
|
||||||
|
memmove(&args->argv[i],
|
||||||
|
&args->argv[i+1],
|
||||||
|
args->argc * sizeof(args->argv[i]));
|
||||||
|
args->argc--;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
396
CCache/ccache.1
Normal file
396
CCache/ccache.1
Normal file
|
|
@ -0,0 +1,396 @@
|
||||||
|
.TH "ccache" "1" "April 2002" "" ""
|
||||||
|
.SH "NAME"
|
||||||
|
ccache \- a fast compiler cache
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.PP
|
||||||
|
ccache [OPTION]
|
||||||
|
.PP
|
||||||
|
ccache <compiler> [COMPILER OPTIONS]
|
||||||
|
.PP
|
||||||
|
<compiler> [COMPILER OPTIONS]
|
||||||
|
.PP
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
.PP
|
||||||
|
ccache is a compiler cache\&. It speeds up re-compilation of C/C++ code
|
||||||
|
by caching previous compiles and detecting when the same compile is
|
||||||
|
being done again\&.
|
||||||
|
.PP
|
||||||
|
.SH "OPTIONS SUMMARY"
|
||||||
|
.PP
|
||||||
|
Here is a summary of the options to ccache\&.
|
||||||
|
.PP
|
||||||
|
|
||||||
|
.nf
|
||||||
|
|
||||||
|
|
||||||
|
-s show statistics summary
|
||||||
|
-z zero statistics
|
||||||
|
-c run a cache cleanup
|
||||||
|
-C clear the cache completely
|
||||||
|
-F <maxfiles> set maximum files in cache
|
||||||
|
-M <maxsize> set maximum size of cache (use G, M or K)
|
||||||
|
-h this help page
|
||||||
|
-V print version number
|
||||||
|
|
||||||
|
.fi
|
||||||
|
|
||||||
|
|
||||||
|
.PP
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.PP
|
||||||
|
These options only apply when you invoke ccache as "ccache"\&. When
|
||||||
|
invoked as a compiler none of these options apply\&. In that case your
|
||||||
|
normal compiler options apply and you should refer to your compilers
|
||||||
|
documentation\&.
|
||||||
|
.PP
|
||||||
|
.IP "\fB-h\fP"
|
||||||
|
Print a options summary page
|
||||||
|
.IP
|
||||||
|
.IP "\fB-s\fP"
|
||||||
|
Print the current statistics summary for the cache\&. The
|
||||||
|
statistics are stored spread across the subdirectories of the
|
||||||
|
cache\&. Using "ccache -s" adds up the statistics across all
|
||||||
|
subdirectories and prints the totals\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fB-z\fP"
|
||||||
|
Zero the cache statistics\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fB-V\fP"
|
||||||
|
Print the ccache version number
|
||||||
|
.IP
|
||||||
|
.IP "\fB-c\fP"
|
||||||
|
Clean the cache and re-calculate the cache file count and
|
||||||
|
size totals\&. Normally the -c option should not be necessary as ccache
|
||||||
|
keeps the cache below the specified limits at runtime and keeps
|
||||||
|
statistics up to date on each compile\&. This option is mostly useful
|
||||||
|
if you manually modify the cache contents or believe that the cache
|
||||||
|
size statistics may be inaccurate\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fB-C\fP"
|
||||||
|
Clear the entire cache, removing all cached files\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fB-F maxfiles\fP"
|
||||||
|
This sets the maximum number of files allowed in
|
||||||
|
the cache\&. The value is stored inside the cache directory and applies
|
||||||
|
to all future compiles\&. Due to the way the value is stored the actual
|
||||||
|
value used is always rounded down to the nearest multiple of 16\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fB-M maxsize\fP"
|
||||||
|
This sets the maximum cache size\&. You can specify
|
||||||
|
a value in gigabytes, megabytes or kilobytes by appending a G, M or K
|
||||||
|
to the value\&. The default is gigabytes\&. The actual value stored is
|
||||||
|
rounded down to the nearest multiple of 16 kilobytes\&.
|
||||||
|
.IP
|
||||||
|
.PP
|
||||||
|
.SH "INSTALLATION"
|
||||||
|
.PP
|
||||||
|
There are two ways to use ccache\&. You can either prefix your compile
|
||||||
|
commands with "ccache" or you can create a symbolic link between
|
||||||
|
ccache and the names of your compilers\&. 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 compiles\&.
|
||||||
|
.PP
|
||||||
|
To install for usage by the first method just copy ccache to somewhere
|
||||||
|
in your path\&.
|
||||||
|
.PP
|
||||||
|
To install for the second method do something like this:
|
||||||
|
|
||||||
|
.nf
|
||||||
|
|
||||||
|
|
||||||
|
cp ccache /usr/local/bin/
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/gcc
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/g++
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/cc
|
||||||
|
|
||||||
|
.fi
|
||||||
|
|
||||||
|
|
||||||
|
This will work as long as /usr/local/bin comes before the path to gcc
|
||||||
|
(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\&.
|
||||||
|
.PP
|
||||||
|
Note! Do not use a hard link, use a symbolic link\&. A hardlink will
|
||||||
|
cause "interesting" problems\&.
|
||||||
|
.PP
|
||||||
|
.SH "EXTRA OPTIONS"
|
||||||
|
.PP
|
||||||
|
When run as a compiler front end ccache usually just takes the same
|
||||||
|
command line options as the compiler you are using\&. The only exception
|
||||||
|
to this is the option \&'--ccache-skip\&'\&. That option can be used to tell
|
||||||
|
ccache that the next option is definitely not a input filename, and
|
||||||
|
should be passed along to the compiler as-is\&.
|
||||||
|
.PP
|
||||||
|
The reason this can be important is that ccache does need to parse the
|
||||||
|
command line and determine what is an input filename and what is a
|
||||||
|
compiler option, as it needs the input filename to determine the name
|
||||||
|
of the resulting object file (among other things)\&. The heuristic
|
||||||
|
ccache uses in this parse is that any string on the command line that
|
||||||
|
exists as a file is treated as an input file name (usually a C
|
||||||
|
file)\&. By using --ccache-skip you can force an option to not be
|
||||||
|
treated as an input file name and instead be passed along to the
|
||||||
|
compiler as a command line option\&.
|
||||||
|
.PP
|
||||||
|
.SH "ENVIRONMENT VARIABLES"
|
||||||
|
.PP
|
||||||
|
ccache uses a number of environment variables to control operation\&. In
|
||||||
|
most cases you won\&'t need any of these as the defaults will be fine\&.
|
||||||
|
.PP
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_DIR\fP"
|
||||||
|
the CCACHE_DIR environment variable specifies
|
||||||
|
where ccache will keep its cached compiler output\&. The default is
|
||||||
|
"$HOME/\&.ccache"\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_TEMPDIR\fP"
|
||||||
|
the CCACHE_TEMPDIR environment variable specifies
|
||||||
|
where ccache will put temporary files\&. The default is the same as
|
||||||
|
CCACHE_DIR\&. Note that the CCACHE_TEMPDIR path must be on the same
|
||||||
|
filesystem as the CCACHE_DIR path, so that renames of files between
|
||||||
|
the two directories can work\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_LOGFILE\fP"
|
||||||
|
If you set the CCACHE_LOGFILE environment
|
||||||
|
variable then ccache will write some log information on cache hits
|
||||||
|
and misses in that file\&. This is useful for tracking down problems\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_PATH\fP"
|
||||||
|
You can optionally set CCACHE_PATH to a colon
|
||||||
|
separated path where ccache will look for the real compilers\&. If you
|
||||||
|
don\&'t do this then ccache will look for the first executable matching
|
||||||
|
the compiler name in the normal PATH that isn\&'t a symbolic link to
|
||||||
|
ccache itself\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_CC\fP"
|
||||||
|
You can optionally set CCACHE_CC to force the name
|
||||||
|
of the compiler to use\&. If you don\&'t do this then ccache works it out
|
||||||
|
from the command line\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_PREFIX\fP"
|
||||||
|
This option adds a prefix to the command line
|
||||||
|
that ccache runs when invoking the compiler\&. Also see the section
|
||||||
|
below on using ccache with distcc\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_DISABLE\fP"
|
||||||
|
If you set the environment variable
|
||||||
|
CCACHE_DISABLE then ccache will just call the real compiler,
|
||||||
|
bypassing the cache completely\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_READONLY\fP"
|
||||||
|
the CCACHE_READONLY environment variable
|
||||||
|
tells ccache to attempt to use existing cached object files, but not
|
||||||
|
to try to add anything new to the cache\&. If you are using this because
|
||||||
|
your CCACHE_DIR is read-only, then you may find that you also need to
|
||||||
|
set CCACHE_TEMPDIR as otherwise ccache will fail to create the
|
||||||
|
temporary files\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_CPP2\fP"
|
||||||
|
If you set the environment variable CCACHE_CPP2
|
||||||
|
then ccache will not use the optimisation of avoiding the 2nd call to
|
||||||
|
the pre-processor by compiling the pre-processed 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 the intermediate filename extensions used in
|
||||||
|
this optimisation, in which case this option could allow ccache to be
|
||||||
|
used\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_NOSTATS\fP"
|
||||||
|
If you set the environment variable
|
||||||
|
CCACHE_NOSTATS then ccache will not update the statistics files on
|
||||||
|
each compile\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_NLEVELS\fP"
|
||||||
|
The environment variable CCACHE_NLEVELS allows
|
||||||
|
you to choose the number of levels of hash in the cache directory\&. The
|
||||||
|
default is 2\&. The minimum is 1 and the maximum is 8\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_HARDLINK\fP"
|
||||||
|
If you set the environment variable
|
||||||
|
CCACHE_HARDLINK then ccache will attempt to use hard links from the
|
||||||
|
cache directory when creating the compiler output rather than using a
|
||||||
|
file copy\&. Using hard links is faster, but can confuse programs like
|
||||||
|
\&'make\&' that rely on modification times\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_RECACHE\fP"
|
||||||
|
This forces ccache to not use any cached
|
||||||
|
results, even if it finds them\&. New results are still cached, but
|
||||||
|
existing cache entries are ignored\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_UMASK\fP"
|
||||||
|
This sets the umask for ccache and all child
|
||||||
|
processes (such as the compiler)\&. This is mostly useful when you wish
|
||||||
|
to share your cache with other users\&. Note that this also affects the
|
||||||
|
file permissions set on the object files created from your
|
||||||
|
compilations\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_HASHDIR\fP"
|
||||||
|
This tells ccache to hash the current working
|
||||||
|
directory when calculating the hash that is used to distinguish two
|
||||||
|
compiles\&. This prevents a problem with the storage of the current
|
||||||
|
working directory in the debug info of a 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\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_UNIFY\fP"
|
||||||
|
If you set the environment variable CCACHE_UNIFY
|
||||||
|
then ccache will use the C/C++ unifier when hashing the pre-processor
|
||||||
|
output if -g is not used in the compile\&. The unifier is slower than a
|
||||||
|
normal hash, so setting this environment variable loses a little bit
|
||||||
|
of speed, but it means that ccache can take advantage of not
|
||||||
|
recompiling when the changes to the source code consist of
|
||||||
|
reformatting only\&. Note that using CCACHE_UNIFY changes the hash, so
|
||||||
|
cached compiles with CCACHE_UNIFY set cannot be used when
|
||||||
|
CCACHE_UNIFY is not set and vice versa\&. The reason the unifier is off
|
||||||
|
by default is that it can give incorrect line number information in
|
||||||
|
compiler warning messages\&.
|
||||||
|
.IP
|
||||||
|
.IP "\fBCCACHE_EXTENSION\fP"
|
||||||
|
Normally ccache tries to automatically
|
||||||
|
determine the extension to use for intermediate C pre-processor files
|
||||||
|
based on the type of file being compiled\&. Unfortunately this sometimes
|
||||||
|
doesn\&'t work, for example when using the aCC compiler on HP-UX\&. On
|
||||||
|
systems like this you can use the CCACHE_EXTENSION option to override
|
||||||
|
the default\&. On HP-UX set this environment variable to "i" if you use
|
||||||
|
the aCC compiler\&.
|
||||||
|
.IP
|
||||||
|
.PP
|
||||||
|
.SH "CACHE SIZE MANAGEMENT"
|
||||||
|
.PP
|
||||||
|
By default ccache has a one gigabyte limit on the cache size and no
|
||||||
|
maximum number of files\&. You can set a different limit using the
|
||||||
|
"ccache -M" and "ccache -F" options, which set the size and number of
|
||||||
|
files limits\&.
|
||||||
|
.PP
|
||||||
|
When these limits are reached ccache will reduce the cache to 20%
|
||||||
|
below the numbers you specified in order to avoid doing the cache
|
||||||
|
clean operation too often\&.
|
||||||
|
.PP
|
||||||
|
.SH "HOW IT WORKS"
|
||||||
|
.PP
|
||||||
|
The basic idea is to detect when you are compiling exactly the same
|
||||||
|
code a 2nd time and use the previously compiled output\&. You detect
|
||||||
|
that it is the same code by forming a hash of:
|
||||||
|
.PP
|
||||||
|
.IP o
|
||||||
|
the pre-processor output from running the compiler with -E
|
||||||
|
.IP o
|
||||||
|
the command line options
|
||||||
|
.IP o
|
||||||
|
the real compilers size and modification time
|
||||||
|
.IP o
|
||||||
|
any stderr output generated by the compiler
|
||||||
|
.PP
|
||||||
|
These are hashed using md4 (a strong hash) and a cache file is formed
|
||||||
|
based on that hash result\&. When the same compilation is done a second
|
||||||
|
time ccache is able to supply the correct compiler output (including
|
||||||
|
all warnings etc) from the cache\&.
|
||||||
|
.PP
|
||||||
|
ccache has been carefully written to always produce exactly the same
|
||||||
|
compiler output that you would get without the cache\&. If you ever
|
||||||
|
discover a case where ccache changes the output of your compiler then
|
||||||
|
please let me know\&.
|
||||||
|
.PP
|
||||||
|
.SH "USING CCACHE WITH DISTCC"
|
||||||
|
.PP
|
||||||
|
distcc is a very useful program for distributing compilation across a
|
||||||
|
range of compiler servers\&. It is often useful to combine distcc with
|
||||||
|
ccache, so that compiles that are done are sped up by distcc, but that
|
||||||
|
ccache avoids the compile completely where possible\&.
|
||||||
|
.PP
|
||||||
|
To use distcc with ccache I recommend using the CCACHE_PREFIX
|
||||||
|
option\&. You just need to set the environment variable CCACHE_PREFIX to
|
||||||
|
\&'distcc\&' and ccache will prefix the command line used with the
|
||||||
|
compiler with the command \&'distcc\&'\&.
|
||||||
|
.PP
|
||||||
|
.SH "SHARING A CACHE"
|
||||||
|
.PP
|
||||||
|
A group of developers can increase the cache hit rate by sharing a
|
||||||
|
cache directory\&. The hard links however cause unwanted side effects,
|
||||||
|
as all links to a cached file share the file\&'s modification timestamp\&.
|
||||||
|
This results in false dependencies to be triggered by timestamp-based
|
||||||
|
build systems whenever another user links to an existing
|
||||||
|
file\&. Typically, users will see that their libraries and binaries are
|
||||||
|
relinked without reason\&. To share a cache without side effects, the
|
||||||
|
following conditions need to be met:
|
||||||
|
.PP
|
||||||
|
.IP o
|
||||||
|
Use the same \fBCCACHE_DIR\fP environment variable setting
|
||||||
|
.IP o
|
||||||
|
Set the \fBCCACHE_NOLINK\fP environment variable
|
||||||
|
.IP o
|
||||||
|
Make sure everyone sets the CCACHE_UMASK environment variable
|
||||||
|
to 002, this ensures that cached files are accessible to everyone in
|
||||||
|
the group\&.
|
||||||
|
.IP o
|
||||||
|
Make sure that all users have write permission in the entire
|
||||||
|
cache directory (and that you trust all users of the shared cache)\&.
|
||||||
|
.IP o
|
||||||
|
Make sure that the setgid bit is set on all directories in the
|
||||||
|
cache\&. This tells the filesystem to inherit group ownership for new
|
||||||
|
directories\&. The command "chmod g+s `find $CCACHE_DIR -type d`" might
|
||||||
|
be useful for this\&.
|
||||||
|
.PP
|
||||||
|
.SH "HISTORY"
|
||||||
|
.PP
|
||||||
|
ccache was inspired by the compilercache shell script script written
|
||||||
|
by Erik Thiele and I would like to thank him for an excellent piece of
|
||||||
|
work\&. See
|
||||||
|
http://www\&.erikyyy\&.de/compilercache/
|
||||||
|
for the Erik\&'s scripts\&.
|
||||||
|
.PP
|
||||||
|
I wrote ccache because I wanted to get a bit more speed out of a
|
||||||
|
compiler cache and I wanted to remove some of the limitations of the
|
||||||
|
shell-script version\&.
|
||||||
|
.PP
|
||||||
|
.SH "DIFFERENCES FROM COMPILERCACHE"
|
||||||
|
.PP
|
||||||
|
The biggest differences between Erik\&'s compilercache script and ccache
|
||||||
|
are:
|
||||||
|
.IP o
|
||||||
|
ccache is written in C, which makes it a bit faster (calling out to
|
||||||
|
external programs is mostly what slowed down the scripts)\&.
|
||||||
|
.IP o
|
||||||
|
ccache can automatically find the real compiler
|
||||||
|
.IP o
|
||||||
|
ccache keeps statistics on hits/misses
|
||||||
|
.IP o
|
||||||
|
ccache can do automatic cache management
|
||||||
|
.IP o
|
||||||
|
ccache can cache compiler output that includes warnings\&. In many
|
||||||
|
cases this gives ccache a much higher cache hit rate\&.
|
||||||
|
.IP o
|
||||||
|
ccache can handle a much wider ranger of compiler options
|
||||||
|
.IP o
|
||||||
|
ccache avoids a double call to cpp on a cache miss
|
||||||
|
.PP
|
||||||
|
.SH "BUGS"
|
||||||
|
.PP
|
||||||
|
When the cache is stored on an NFS filesystem, the filesystem must be
|
||||||
|
exported with the \fBno_subtree_check\fP option to make renames between
|
||||||
|
directories reliable\&.
|
||||||
|
.PP
|
||||||
|
.SH "CREDITS"
|
||||||
|
.PP
|
||||||
|
Thanks to the following people for their contributions to ccache
|
||||||
|
.IP o
|
||||||
|
Erik Thiele for the original compilercache script
|
||||||
|
.IP o
|
||||||
|
Luciano Rocha for the idea of compiling the pre-processor output
|
||||||
|
to avoid a 2nd cpp pass
|
||||||
|
.IP o
|
||||||
|
Paul Russell for many suggestions and the debian packaging
|
||||||
|
.PP
|
||||||
|
.SH "AUTHOR"
|
||||||
|
.PP
|
||||||
|
ccache was written by Andrew Tridgell
|
||||||
|
http://samba\&.org/~tridge/
|
||||||
|
.PP
|
||||||
|
If you wish to report a problem or make a suggestion then please email
|
||||||
|
bugs@ccache\&.samba\&.org
|
||||||
|
.PP
|
||||||
|
ccache is released under the GNU General Public License version 2 or
|
||||||
|
later\&. Please see the file COPYING for license details\&.
|
||||||
1034
CCache/ccache.c
Normal file
1034
CCache/ccache.c
Normal file
File diff suppressed because it is too large
Load diff
159
CCache/ccache.h
Normal file
159
CCache/ccache.h
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
#define CCACHE_VERSION "2.4"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <utime.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#ifdef HAVE_PWD_H
|
||||||
|
#include <pwd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define STATUS_NOTFOUND 3
|
||||||
|
#define STATUS_FATAL 4
|
||||||
|
#define STATUS_NOCACHE 5
|
||||||
|
|
||||||
|
#define MYNAME "ccache"
|
||||||
|
|
||||||
|
#define LIMIT_MULTIPLE 0.8
|
||||||
|
|
||||||
|
/* default maximum cache size */
|
||||||
|
#ifndef DEFAULT_MAXSIZE
|
||||||
|
#define DEFAULT_MAXSIZE (1000*1000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum stats {
|
||||||
|
STATS_NONE=0,
|
||||||
|
STATS_STDOUT,
|
||||||
|
STATS_STATUS,
|
||||||
|
STATS_ERROR,
|
||||||
|
STATS_TOCACHE,
|
||||||
|
STATS_PREPROCESSOR,
|
||||||
|
STATS_COMPILER,
|
||||||
|
STATS_MISSING,
|
||||||
|
STATS_CACHED,
|
||||||
|
STATS_ARGS,
|
||||||
|
STATS_LINK,
|
||||||
|
STATS_NUMFILES,
|
||||||
|
STATS_TOTALSIZE,
|
||||||
|
STATS_MAXFILES,
|
||||||
|
STATS_MAXSIZE,
|
||||||
|
STATS_NOTC,
|
||||||
|
STATS_DEVICE,
|
||||||
|
STATS_NOINPUT,
|
||||||
|
STATS_MULTIPLE,
|
||||||
|
STATS_CONFTEST,
|
||||||
|
STATS_UNSUPPORTED,
|
||||||
|
STATS_OUTSTDOUT,
|
||||||
|
|
||||||
|
STATS_END
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef unsigned uint32;
|
||||||
|
|
||||||
|
#include "mdfour.h"
|
||||||
|
|
||||||
|
void hash_start(void);
|
||||||
|
void hash_string(const char *s);
|
||||||
|
void hash_int(int x);
|
||||||
|
void hash_file(const char *fname);
|
||||||
|
char *hash_result(void);
|
||||||
|
void hash_buffer(const char *s, int len);
|
||||||
|
|
||||||
|
void cc_log(const char *format, ...);
|
||||||
|
void fatal(const char *msg);
|
||||||
|
|
||||||
|
void copy_fd(int fd_in, int fd_out);
|
||||||
|
int copy_file(const char *src, const char *dest);
|
||||||
|
|
||||||
|
int create_dir(const char *dir);
|
||||||
|
void x_asprintf(char **ptr, const char *format, ...);
|
||||||
|
char *x_strdup(const char *s);
|
||||||
|
void *x_realloc(void *ptr, size_t size);
|
||||||
|
void *x_malloc(size_t size);
|
||||||
|
void traverse(const char *dir, void (*fn)(const char *, struct stat *));
|
||||||
|
char *str_basename(const char *s);
|
||||||
|
char *dirname(char *s);
|
||||||
|
int lock_fd(int fd);
|
||||||
|
size_t file_size(struct stat *st);
|
||||||
|
int safe_open(const char *fname);
|
||||||
|
char *x_realpath(const char *path);
|
||||||
|
char *gnu_getcwd(void);
|
||||||
|
int create_empty_file(const char *fname);
|
||||||
|
const char *get_home_directory(void);
|
||||||
|
|
||||||
|
void stats_update(enum stats stat);
|
||||||
|
void stats_zero(void);
|
||||||
|
void stats_summary(void);
|
||||||
|
void stats_tocache(size_t size);
|
||||||
|
void stats_read(const char *stats_file, unsigned counters[STATS_END]);
|
||||||
|
void stats_set_limits(long maxfiles, long maxsize);
|
||||||
|
size_t value_units(const char *s);
|
||||||
|
void display_size(unsigned v);
|
||||||
|
void stats_set_sizes(const char *dir, size_t num_files, size_t total_size);
|
||||||
|
|
||||||
|
int unify_hash(const char *fname);
|
||||||
|
|
||||||
|
#ifndef HAVE_VASPRINTF
|
||||||
|
int vasprintf(char **, const char *, va_list );
|
||||||
|
#endif
|
||||||
|
#ifndef HAVE_ASPRINTF
|
||||||
|
int asprintf(char **ptr, const char *format, ...);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_SNPRINTF
|
||||||
|
int snprintf(char *,size_t ,const char *, ...);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cleanup_dir(const char *dir, size_t maxfiles, size_t maxsize);
|
||||||
|
void cleanup_all(const char *dir);
|
||||||
|
void wipe_all(const char *dir);
|
||||||
|
|
||||||
|
int execute(char **argv,
|
||||||
|
const char *path_stdout,
|
||||||
|
const char *path_stderr);
|
||||||
|
char *find_executable(const char *name, const char *exclude_name);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char **argv;
|
||||||
|
int argc;
|
||||||
|
} ARGS;
|
||||||
|
|
||||||
|
|
||||||
|
ARGS *args_init(int , char **);
|
||||||
|
void args_add(ARGS *args, const char *s);
|
||||||
|
void args_add_prefix(ARGS *args, const char *s);
|
||||||
|
void args_pop(ARGS *args, int n);
|
||||||
|
void args_strip(ARGS *args, const char *prefix);
|
||||||
|
void args_remove_first(ARGS *args);
|
||||||
|
|
||||||
|
#if HAVE_COMPAR_FN_T
|
||||||
|
#define COMPAR_FN_T __compar_fn_t
|
||||||
|
#else
|
||||||
|
typedef int (*COMPAR_FN_T)(const void *, const void *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
#undef HAVE_MKSTEMP
|
||||||
|
#endif
|
||||||
352
CCache/ccache.yo
Normal file
352
CCache/ccache.yo
Normal file
|
|
@ -0,0 +1,352 @@
|
||||||
|
mailto(bugs@ccache.samba.org)
|
||||||
|
manpage(ccache)(1)(April 2002)()()
|
||||||
|
manpagename(ccache)(a fast compiler cache)
|
||||||
|
manpagesynopsis()
|
||||||
|
|
||||||
|
ccache [OPTION]
|
||||||
|
|
||||||
|
ccache <compiler> [COMPILER OPTIONS]
|
||||||
|
|
||||||
|
<compiler> [COMPILER OPTIONS]
|
||||||
|
|
||||||
|
manpagedescription()
|
||||||
|
|
||||||
|
ccache is a compiler cache. It speeds up re-compilation of C/C++ code
|
||||||
|
by caching previous compiles and detecting when the same compile is
|
||||||
|
being done again.
|
||||||
|
|
||||||
|
manpagesection(OPTIONS SUMMARY)
|
||||||
|
|
||||||
|
Here is a summary of the options to ccache.
|
||||||
|
|
||||||
|
verb(
|
||||||
|
-s show statistics summary
|
||||||
|
-z zero statistics
|
||||||
|
-c run a cache cleanup
|
||||||
|
-C clear the cache completely
|
||||||
|
-F <maxfiles> set maximum files in cache
|
||||||
|
-M <maxsize> set maximum size of cache (use G, M or K)
|
||||||
|
-h this help page
|
||||||
|
-V print version number
|
||||||
|
)
|
||||||
|
|
||||||
|
manpageoptions()
|
||||||
|
|
||||||
|
These options only apply when you invoke ccache as "ccache". When
|
||||||
|
invoked as a compiler none of these options apply. In that case your
|
||||||
|
normal compiler options apply and you should refer to your compilers
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
startdit()
|
||||||
|
dit(bf(-h)) Print a options summary page
|
||||||
|
|
||||||
|
dit(bf(-s)) Print the current statistics summary for the cache. The
|
||||||
|
statistics are stored spread across the subdirectories of the
|
||||||
|
cache. Using "ccache -s" adds up the statistics across all
|
||||||
|
subdirectories and prints the totals.
|
||||||
|
|
||||||
|
dit(bf(-z)) Zero the cache statistics.
|
||||||
|
|
||||||
|
dit(bf(-V)) Print the ccache version number
|
||||||
|
|
||||||
|
dit(bf(-c)) Clean the cache and re-calculate the cache file count and
|
||||||
|
size totals. Normally the -c option should not be necessary as ccache
|
||||||
|
keeps the cache below the specified limits at runtime and keeps
|
||||||
|
statistics up to date on each compile. This option is mostly useful
|
||||||
|
if you manually modify the cache contents or believe that the cache
|
||||||
|
size statistics may be inaccurate.
|
||||||
|
|
||||||
|
dit(bf(-C)) Clear the entire cache, removing all cached files.
|
||||||
|
|
||||||
|
dit(bf(-F maxfiles)) This sets the maximum number of files allowed in
|
||||||
|
the cache. The value is stored inside the cache directory and applies
|
||||||
|
to all future compiles. Due to the way the value is stored the actual
|
||||||
|
value used is always rounded down to the nearest multiple of 16.
|
||||||
|
|
||||||
|
dit(bf(-M maxsize)) This sets the maximum cache size. You can specify
|
||||||
|
a value in gigabytes, megabytes or kilobytes by appending a G, M or K
|
||||||
|
to the value. The default is gigabytes. The actual value stored is
|
||||||
|
rounded down to the nearest multiple of 16 kilobytes.
|
||||||
|
|
||||||
|
enddit()
|
||||||
|
|
||||||
|
manpagesection(INSTALLATION)
|
||||||
|
|
||||||
|
There are two ways to use ccache. You can either prefix your compile
|
||||||
|
commands with "ccache" or you can create a symbolic link between
|
||||||
|
ccache and the names of your compilers. 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 compiles.
|
||||||
|
|
||||||
|
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:
|
||||||
|
verb(
|
||||||
|
cp ccache /usr/local/bin/
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/gcc
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/g++
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/cc
|
||||||
|
)
|
||||||
|
This will work as long as /usr/local/bin comes before the path to gcc
|
||||||
|
(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 hardlink will
|
||||||
|
cause "interesting" problems.
|
||||||
|
|
||||||
|
manpagesection(EXTRA OPTIONS)
|
||||||
|
|
||||||
|
When run as a compiler front end ccache usually just takes the same
|
||||||
|
command line options as the compiler you are using. The only exception
|
||||||
|
to this is the option '--ccache-skip'. That option can be used to tell
|
||||||
|
ccache that the next option is definitely not a input filename, and
|
||||||
|
should be passed along to the compiler as-is.
|
||||||
|
|
||||||
|
The reason this can be important is that ccache does need to parse the
|
||||||
|
command line and determine what is an input filename and what is a
|
||||||
|
compiler option, as it needs the input filename to determine the name
|
||||||
|
of the resulting object file (among other things). The heuristic
|
||||||
|
ccache uses in this parse is that any string on the command line that
|
||||||
|
exists as a file is treated as an input file name (usually a C
|
||||||
|
file). By using --ccache-skip you can force an option to not be
|
||||||
|
treated as an input file name and instead be passed along to the
|
||||||
|
compiler as a command line option.
|
||||||
|
|
||||||
|
manpagesection(ENVIRONMENT VARIABLES)
|
||||||
|
|
||||||
|
ccache uses a number of environment variables to control operation. In
|
||||||
|
most cases you won't need any of these as the defaults will be fine.
|
||||||
|
|
||||||
|
startdit()
|
||||||
|
|
||||||
|
dit(bf(CCACHE_DIR)) the CCACHE_DIR environment variable specifies
|
||||||
|
where ccache will keep its cached compiler output. The default is
|
||||||
|
"$HOME/.ccache".
|
||||||
|
|
||||||
|
dit(bf(CCACHE_TEMPDIR)) the CCACHE_TEMPDIR environment variable specifies
|
||||||
|
where ccache will put temporary files. The default is the same as
|
||||||
|
CCACHE_DIR. Note that the CCACHE_TEMPDIR path must be on the same
|
||||||
|
filesystem as the CCACHE_DIR path, so that renames of files between
|
||||||
|
the two directories can work.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_LOGFILE)) If you set the CCACHE_LOGFILE environment
|
||||||
|
variable then ccache will write some log information on cache hits
|
||||||
|
and misses in that file. This is useful for tracking down problems.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_PATH)) You can optionally set CCACHE_PATH to a colon
|
||||||
|
separated path where ccache will look for the real compilers. If you
|
||||||
|
don't do this then ccache will look for the first executable matching
|
||||||
|
the compiler name in the normal PATH that isn't a symbolic link to
|
||||||
|
ccache itself.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_CC)) You can optionally set CCACHE_CC to force the name
|
||||||
|
of the compiler to use. If you don't do this then ccache works it out
|
||||||
|
from the command line.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_PREFIX)) This option adds a prefix to the command line
|
||||||
|
that ccache runs when invoking the compiler. Also see the section
|
||||||
|
below on using ccache with distcc.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_DISABLE)) If you set the environment variable
|
||||||
|
CCACHE_DISABLE then ccache will just call the real compiler,
|
||||||
|
bypassing the cache completely.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_READONLY)) the CCACHE_READONLY environment variable
|
||||||
|
tells ccache to attempt to use existing cached object files, but not
|
||||||
|
to try to add anything new to the cache. If you are using this because
|
||||||
|
your CCACHE_DIR is read-only, then you may find that you also need to
|
||||||
|
set CCACHE_TEMPDIR as otherwise ccache will fail to create the
|
||||||
|
temporary files.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_CPP2)) If you set the environment variable CCACHE_CPP2
|
||||||
|
then ccache will not use the optimisation of avoiding the 2nd call to
|
||||||
|
the pre-processor by compiling the pre-processed 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 the intermediate filename extensions used in
|
||||||
|
this optimisation, in which case this option could allow ccache to be
|
||||||
|
used.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_NOSTATS)) If you set the environment variable
|
||||||
|
CCACHE_NOSTATS then ccache will not update the statistics files on
|
||||||
|
each compile.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_NLEVELS)) The environment variable CCACHE_NLEVELS allows
|
||||||
|
you to choose the number of levels of hash in the cache directory. The
|
||||||
|
default is 2. The minimum is 1 and the maximum is 8.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_HARDLINK)) If you set the environment variable
|
||||||
|
CCACHE_HARDLINK then ccache will attempt to use hard links from the
|
||||||
|
cache directory when creating the compiler output rather than using a
|
||||||
|
file copy. Using hard links is faster, but can confuse programs like
|
||||||
|
'make' that rely on modification times.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_RECACHE)) This forces ccache to not use any cached
|
||||||
|
results, even if it finds them. New results are still cached, but
|
||||||
|
existing cache entries are ignored.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_UMASK)) This sets the umask for ccache and all child
|
||||||
|
processes (such as the compiler). This is mostly useful when you wish
|
||||||
|
to share your cache with other users. Note that this also affects the
|
||||||
|
file permissions set on the object files created from your
|
||||||
|
compilations.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_HASHDIR)) This tells ccache to hash the current working
|
||||||
|
directory when calculating the hash that is used to distinguish two
|
||||||
|
compiles. This prevents a problem with the storage of the current
|
||||||
|
working directory in the debug info of a 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.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_UNIFY)) If you set the environment variable CCACHE_UNIFY
|
||||||
|
then ccache will use the C/C++ unifier when hashing the pre-processor
|
||||||
|
output if -g is not used in the compile. The unifier is slower than a
|
||||||
|
normal hash, so setting this environment variable loses a little bit
|
||||||
|
of speed, but it means that ccache can take advantage of not
|
||||||
|
recompiling when the changes to the source code consist of
|
||||||
|
reformatting only. Note that using CCACHE_UNIFY changes the hash, so
|
||||||
|
cached compiles with CCACHE_UNIFY set cannot be used when
|
||||||
|
CCACHE_UNIFY is not set and vice versa. The reason the unifier is off
|
||||||
|
by default is that it can give incorrect line number information in
|
||||||
|
compiler warning messages.
|
||||||
|
|
||||||
|
dit(bf(CCACHE_EXTENSION)) Normally ccache tries to automatically
|
||||||
|
determine the extension to use for intermediate C pre-processor files
|
||||||
|
based on the type of file being compiled. Unfortunately this sometimes
|
||||||
|
doesn't work, for example when using the aCC compiler on HP-UX. On
|
||||||
|
systems like this you can use the CCACHE_EXTENSION option to override
|
||||||
|
the default. On HP-UX set this environment variable to "i" if you use
|
||||||
|
the aCC compiler.
|
||||||
|
|
||||||
|
enddit()
|
||||||
|
|
||||||
|
manpagesection(CACHE SIZE MANAGEMENT)
|
||||||
|
|
||||||
|
By default ccache has a one gigabyte limit on the cache size and no
|
||||||
|
maximum number of files. You can set a different limit using the
|
||||||
|
"ccache -M" and "ccache -F" options, which set the size and number of
|
||||||
|
files limits.
|
||||||
|
|
||||||
|
When these limits are reached ccache will reduce the cache to 20%
|
||||||
|
below the numbers you specified in order to avoid doing the cache
|
||||||
|
clean operation too often.
|
||||||
|
|
||||||
|
manpagesection(HOW IT WORKS)
|
||||||
|
|
||||||
|
The basic idea is to detect when you are compiling exactly the same
|
||||||
|
code a 2nd time and use the previously compiled output. You detect
|
||||||
|
that it is the same code by forming a hash of:
|
||||||
|
|
||||||
|
itemize(
|
||||||
|
it() the pre-processor output from running the compiler with -E
|
||||||
|
it() the command line options
|
||||||
|
it() the real compilers size and modification time
|
||||||
|
it() any stderr output generated by the compiler
|
||||||
|
)
|
||||||
|
|
||||||
|
These are hashed using md4 (a strong hash) and a cache file is formed
|
||||||
|
based on that hash result. When the same compilation is done a second
|
||||||
|
time ccache is able to supply the correct compiler output (including
|
||||||
|
all warnings etc) from the cache.
|
||||||
|
|
||||||
|
ccache has been carefully written to always produce exactly the same
|
||||||
|
compiler output that you would get without the cache. If you ever
|
||||||
|
discover a case where ccache changes the output of your compiler then
|
||||||
|
please let me know.
|
||||||
|
|
||||||
|
manpagesection(USING CCACHE WITH DISTCC)
|
||||||
|
|
||||||
|
distcc is a very useful program for distributing compilation across a
|
||||||
|
range of compiler servers. It is often useful to combine distcc with
|
||||||
|
ccache, so that compiles that are done are sped up by distcc, but that
|
||||||
|
ccache avoids the compile completely where possible.
|
||||||
|
|
||||||
|
To use distcc with ccache I recommend using the CCACHE_PREFIX
|
||||||
|
option. You just need to set the environment variable CCACHE_PREFIX to
|
||||||
|
'distcc' and ccache will prefix the command line used with the
|
||||||
|
compiler with the command 'distcc'.
|
||||||
|
|
||||||
|
manpagesection(SHARING A CACHE)
|
||||||
|
|
||||||
|
A group of developers can increase the cache hit rate by sharing a
|
||||||
|
cache directory. The hard links however cause unwanted side effects,
|
||||||
|
as all links to a cached file share the file's modification timestamp.
|
||||||
|
This results in false dependencies to be triggered by timestamp-based
|
||||||
|
build systems whenever another user links to an existing
|
||||||
|
file. Typically, users will see that their libraries and binaries are
|
||||||
|
relinked without reason. To share a cache without side effects, the
|
||||||
|
following conditions need to be met:
|
||||||
|
|
||||||
|
itemize(
|
||||||
|
it() Use the same bf(CCACHE_DIR) environment variable setting
|
||||||
|
it() Set the bf(CCACHE_NOLINK) environment variable
|
||||||
|
it() Make sure everyone sets the CCACHE_UMASK environment variable
|
||||||
|
to 002, this ensures that cached files are accessible to everyone in
|
||||||
|
the group.
|
||||||
|
it() Make sure that all users have write permission in the entire
|
||||||
|
cache directory (and that you trust all users of the shared cache).
|
||||||
|
it() Make sure that the setgid bit is set on all directories in the
|
||||||
|
cache. This tells the filesystem to inherit group ownership for new
|
||||||
|
directories. The command "chmod g+s `find $CCACHE_DIR -type d`" might
|
||||||
|
be useful for this.
|
||||||
|
)
|
||||||
|
|
||||||
|
manpagesection(HISTORY)
|
||||||
|
|
||||||
|
ccache was inspired by the compilercache shell script script written
|
||||||
|
by Erik Thiele and I would like to thank him for an excellent piece of
|
||||||
|
work. See
|
||||||
|
url(http://www.erikyyy.de/compilercache/)(http://www.erikyyy.de/compilercache/)
|
||||||
|
for the Erik's scripts.
|
||||||
|
|
||||||
|
I wrote ccache because I wanted to get a bit more speed out of a
|
||||||
|
compiler cache and I wanted to remove some of the limitations of the
|
||||||
|
shell-script version.
|
||||||
|
|
||||||
|
manpagesection(DIFFERENCES FROM COMPILERCACHE)
|
||||||
|
|
||||||
|
The biggest differences between Erik's compilercache script and ccache
|
||||||
|
are:
|
||||||
|
itemize(
|
||||||
|
it() ccache is written in C, which makes it a bit faster (calling out to
|
||||||
|
external programs is mostly what slowed down the scripts).
|
||||||
|
it() ccache can automatically find the real compiler
|
||||||
|
it() ccache keeps statistics on hits/misses
|
||||||
|
it() ccache can do automatic cache management
|
||||||
|
it() ccache can cache compiler output that includes warnings. In many
|
||||||
|
cases this gives ccache a much higher cache hit rate.
|
||||||
|
it() ccache can handle a much wider ranger of compiler options
|
||||||
|
it() ccache avoids a double call to cpp on a cache miss
|
||||||
|
)
|
||||||
|
|
||||||
|
manpagesection(BUGS)
|
||||||
|
|
||||||
|
When the cache is stored on an NFS filesystem, the filesystem must be
|
||||||
|
exported with the bf(no_subtree_check) option to make renames between
|
||||||
|
directories reliable.
|
||||||
|
|
||||||
|
manpagesection(CREDITS)
|
||||||
|
|
||||||
|
Thanks to the following people for their contributions to ccache
|
||||||
|
itemize(
|
||||||
|
it() Erik Thiele for the original compilercache script
|
||||||
|
it() Luciano Rocha for the idea of compiling the pre-processor output
|
||||||
|
to avoid a 2nd cpp pass
|
||||||
|
it() Paul Russell for many suggestions and the debian packaging
|
||||||
|
)
|
||||||
|
|
||||||
|
manpageauthor()
|
||||||
|
|
||||||
|
ccache was written by Andrew Tridgell
|
||||||
|
url(http://samba.org/~tridge/)(http://samba.org/~tridge/)
|
||||||
|
|
||||||
|
If you wish to report a problem or make a suggestion then please email
|
||||||
|
bugs@ccache.samba.org
|
||||||
|
|
||||||
|
ccache is released under the GNU General Public License version 2 or
|
||||||
|
later. Please see the file COPYING for license details.
|
||||||
193
CCache/cleanup.c
Normal file
193
CCache/cleanup.c
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) Andrew Tridgell 2002
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
functions to cleanup the cache directory when it gets too large
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ccache.h"
|
||||||
|
|
||||||
|
static struct files {
|
||||||
|
char *fname;
|
||||||
|
time_t mtime;
|
||||||
|
size_t size;
|
||||||
|
} **files;
|
||||||
|
static unsigned allocated;
|
||||||
|
static unsigned num_files;
|
||||||
|
static size_t total_size;
|
||||||
|
static size_t total_files;
|
||||||
|
static size_t size_threshold;
|
||||||
|
static size_t files_threshold;
|
||||||
|
|
||||||
|
/* file comparison function to try to delete the oldest files first */
|
||||||
|
static int files_compare(struct files **f1, struct files **f2)
|
||||||
|
{
|
||||||
|
if ((*f2)->mtime == (*f1)->mtime) {
|
||||||
|
return strcmp((*f2)->fname, (*f1)->fname);
|
||||||
|
}
|
||||||
|
if ((*f2)->mtime > (*f1)->mtime) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 = str_basename(fname);
|
||||||
|
if (strcmp(p, "stats") == 0) {
|
||||||
|
free(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
if (num_files == allocated) {
|
||||||
|
allocated = 10000 + num_files*2;
|
||||||
|
files = (struct files **)x_realloc(files,
|
||||||
|
sizeof(struct files *)*allocated);
|
||||||
|
}
|
||||||
|
|
||||||
|
files[num_files] = (struct files *)x_malloc(sizeof(struct files));
|
||||||
|
files[num_files]->fname = x_strdup(fname);
|
||||||
|
files[num_files]->mtime = st->st_mtime;
|
||||||
|
files[num_files]->size = file_size(st) / 1024;
|
||||||
|
total_size += files[num_files]->size;
|
||||||
|
num_files++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort the files we've found and delete the oldest ones until we are
|
||||||
|
below the thresholds */
|
||||||
|
static void sort_and_clean(void)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if (num_files > 1) {
|
||||||
|
/* sort in ascending data 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++) {
|
||||||
|
if ((size_threshold==0 || total_size < size_threshold) &&
|
||||||
|
(files_threshold==0 || (num_files-i) < files_threshold)) break;
|
||||||
|
|
||||||
|
if (unlink(files[i]->fname) != 0 && errno != ENOENT) {
|
||||||
|
fprintf(stderr, "unlink %s - %s\n",
|
||||||
|
files[i]->fname, strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_size -= files[i]->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_files = num_files - i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cleanup in one cache subdir */
|
||||||
|
void cleanup_dir(const char *dir, size_t maxfiles, size_t maxsize)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
size_threshold = maxsize * LIMIT_MULTIPLE;
|
||||||
|
files_threshold = maxfiles * LIMIT_MULTIPLE;
|
||||||
|
|
||||||
|
num_files = 0;
|
||||||
|
total_size = 0;
|
||||||
|
|
||||||
|
/* build a list of files */
|
||||||
|
traverse(dir, traverse_fn);
|
||||||
|
|
||||||
|
/* clean the cache */
|
||||||
|
sort_and_clean();
|
||||||
|
|
||||||
|
stats_set_sizes(dir, total_files, total_size);
|
||||||
|
|
||||||
|
/* free it up */
|
||||||
|
for (i=0;i<num_files;i++) {
|
||||||
|
free(files[i]->fname);
|
||||||
|
free(files[i]);
|
||||||
|
files[i] = NULL;
|
||||||
|
}
|
||||||
|
if (files) free(files);
|
||||||
|
allocated = 0;
|
||||||
|
files = NULL;
|
||||||
|
|
||||||
|
num_files = 0;
|
||||||
|
total_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cleanup in all cache subdirs */
|
||||||
|
void cleanup_all(const char *dir)
|
||||||
|
{
|
||||||
|
unsigned counters[STATS_END];
|
||||||
|
char *dname, *sfile;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0;i<=0xF;i++) {
|
||||||
|
x_asprintf(&dname, "%s/%1x", dir, i);
|
||||||
|
x_asprintf(&sfile, "%s/%1x/stats", dir, i);
|
||||||
|
|
||||||
|
memset(counters, 0, sizeof(counters));
|
||||||
|
stats_read(sfile, counters);
|
||||||
|
|
||||||
|
cleanup_dir(dname,
|
||||||
|
counters[STATS_MAXFILES],
|
||||||
|
counters[STATS_MAXSIZE]);
|
||||||
|
free(dname);
|
||||||
|
free(sfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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 = str_basename(fname);
|
||||||
|
if (strcmp(p, "stats") == 0) {
|
||||||
|
free(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
|
||||||
|
unlink(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* wipe all cached files in all subdirs */
|
||||||
|
void wipe_all(const char *dir)
|
||||||
|
{
|
||||||
|
char *dname;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0;i<=0xF;i++) {
|
||||||
|
x_asprintf(&dname, "%s/%1x", dir, i);
|
||||||
|
traverse(dir, wipe_fn);
|
||||||
|
free(dname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* and fix the counters */
|
||||||
|
cleanup_all(dir);
|
||||||
|
}
|
||||||
100
CCache/config.h.in
Normal file
100
CCache/config.h.in
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
/* config.h.in. Generated from configure.in by autoheader. */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `asprintf' function. */
|
||||||
|
#undef HAVE_ASPRINTF
|
||||||
|
|
||||||
|
/* */
|
||||||
|
#undef HAVE_C99_VSNPRINTF
|
||||||
|
|
||||||
|
/* */
|
||||||
|
#undef HAVE_COMPAR_FN_T
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <ctype.h> header file. */
|
||||||
|
#undef HAVE_CTYPE_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
|
||||||
|
*/
|
||||||
|
#undef HAVE_DIRENT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `gethostname' function. */
|
||||||
|
#undef HAVE_GETHOSTNAME
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#undef HAVE_INTTYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#undef HAVE_MEMORY_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `mkstemp' function. */
|
||||||
|
#undef HAVE_MKSTEMP
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
|
||||||
|
#undef HAVE_NDIR_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `realpath' function. */
|
||||||
|
#undef HAVE_REALPATH
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `snprintf' function. */
|
||||||
|
#undef HAVE_SNPRINTF
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#undef HAVE_STDINT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#undef HAVE_STDLIB_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#undef HAVE_STRINGS_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#undef HAVE_STRING_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
|
||||||
|
*/
|
||||||
|
#undef HAVE_SYS_DIR_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
|
||||||
|
*/
|
||||||
|
#undef HAVE_SYS_NDIR_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#undef HAVE_SYS_STAT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#undef HAVE_SYS_TYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
|
||||||
|
#undef HAVE_SYS_WAIT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#undef HAVE_UNISTD_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `vasprintf' function. */
|
||||||
|
#undef HAVE_VASPRINTF
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `vsnprintf' function. */
|
||||||
|
#undef HAVE_VSNPRINTF
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#undef PACKAGE_BUGREPORT
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#undef PACKAGE_NAME
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#undef PACKAGE_STRING
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#undef PACKAGE_TARNAME
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#undef PACKAGE_VERSION
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#undef STDC_HEADERS
|
||||||
|
|
||||||
|
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||||
|
#undef TIME_WITH_SYS_TIME
|
||||||
|
|
||||||
|
/* Define _GNU_SOURCE so that we get all necessary prototypes */
|
||||||
|
#undef _GNU_SOURCE
|
||||||
4896
CCache/configure
vendored
Executable file
4896
CCache/configure
vendored
Executable file
File diff suppressed because it is too large
Load diff
72
CCache/configure.in
Normal file
72
CCache/configure.in
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
dnl Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
|
AC_INIT()
|
||||||
|
AC_PREREQ(2.52)
|
||||||
|
|
||||||
|
AC_MSG_NOTICE([Configuring ccache])
|
||||||
|
|
||||||
|
AC_CONFIG_HEADER(config.h)
|
||||||
|
|
||||||
|
dnl Checks for programs.
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_CPP
|
||||||
|
AC_PROG_INSTALL
|
||||||
|
|
||||||
|
AC_DEFINE([_GNU_SOURCE], 1,
|
||||||
|
[Define _GNU_SOURCE so that we get all necessary prototypes])
|
||||||
|
|
||||||
|
# If GCC, turn on warnings.
|
||||||
|
if test "x$GCC" = "xyes"
|
||||||
|
then
|
||||||
|
CFLAGS="$CFLAGS -Wall -W"
|
||||||
|
else
|
||||||
|
CFLAGS="$CFLAGS -O"
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_HEADER_DIRENT
|
||||||
|
AC_HEADER_TIME
|
||||||
|
AC_HEADER_SYS_WAIT
|
||||||
|
|
||||||
|
AC_CHECK_HEADERS(ctype.h strings.h stdlib.h string.h pwd.h)
|
||||||
|
|
||||||
|
AC_CHECK_FUNCS(realpath snprintf vsnprintf vasprintf asprintf mkstemp)
|
||||||
|
AC_CHECK_FUNCS(gethostname getpwuid)
|
||||||
|
|
||||||
|
AC_CACHE_CHECK([for compar_fn_t in stdlib.h],ccache_cv_COMPAR_FN_T, [
|
||||||
|
AC_TRY_COMPILE(
|
||||||
|
[#include <stdlib.h>],
|
||||||
|
[
|
||||||
|
void test_fn(void) { qsort(NULL, 0, 0, (__compar_fn_t)NULL); }
|
||||||
|
],
|
||||||
|
ccache_cv_COMPAR_FN_T=yes,ccache_cv_COMPAR_FN_T=no)])
|
||||||
|
if test x"$ccache_cv_COMPAR_FN_T" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_COMPAR_FN_T, 1, [ ])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_CACHE_CHECK([for C99 vsnprintf],ccache_cv_HAVE_C99_VSNPRINTF,[
|
||||||
|
AC_TRY_RUN([
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
void foo(const char *format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
int len;
|
||||||
|
char buf[5];
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
len = vsnprintf(0, 0, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (len != 5) exit(1);
|
||||||
|
|
||||||
|
if (snprintf(buf, 3, "hello") != 5 || strcmp(buf, "he") != 0) exit(1);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
main() { foo("hello"); }
|
||||||
|
],
|
||||||
|
ccache_cv_HAVE_C99_VSNPRINTF=yes,ccache_cv_HAVE_C99_VSNPRINTF=no,ccache_cv_HAVE_C99_VSNPRINTF=cross)])
|
||||||
|
if test x"$ccache_cv_HAVE_C99_VSNPRINTF" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_C99_VSNPRINTF, 1, [ ])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([Makefile])
|
||||||
|
AC_OUTPUT
|
||||||
129
CCache/execute.c
Normal file
129
CCache/execute.c
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) Andrew Tridgell 2002
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ccache.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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,
|
||||||
|
const char *path_stdout,
|
||||||
|
const char *path_stderr)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid == -1) fatal("Failed to fork");
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
unlink(path_stdout);
|
||||||
|
fd = open(path_stdout, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
|
||||||
|
if (fd == -1) {
|
||||||
|
exit(STATUS_NOCACHE);
|
||||||
|
}
|
||||||
|
dup2(fd, 1);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
unlink(path_stderr);
|
||||||
|
fd = open(path_stderr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
|
||||||
|
if (fd == -1) {
|
||||||
|
exit(STATUS_NOCACHE);
|
||||||
|
}
|
||||||
|
dup2(fd, 2);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
exit(execv(argv[0], argv));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitpid(pid, &status, 0) != pid) {
|
||||||
|
fatal("waitpid failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
char *tok;
|
||||||
|
struct stat st1, st2;
|
||||||
|
|
||||||
|
if (*name == '/') {
|
||||||
|
return x_strdup(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
path = getenv("CCACHE_PATH");
|
||||||
|
if (!path) {
|
||||||
|
path = getenv("PATH");
|
||||||
|
}
|
||||||
|
if (!path) {
|
||||||
|
cc_log("no PATH variable!?\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = x_strdup(path);
|
||||||
|
|
||||||
|
/* search the path looking for the first compiler of the right name
|
||||||
|
that isn't us */
|
||||||
|
for (tok=strtok(path,":"); tok; tok = strtok(NULL, ":")) {
|
||||||
|
char *fname;
|
||||||
|
x_asprintf(&fname, "%s/%s", tok, name);
|
||||||
|
/* look for a normal executable file */
|
||||||
|
if (access(fname, X_OK) == 0 &&
|
||||||
|
lstat(fname, &st1) == 0 &&
|
||||||
|
stat(fname, &st2) == 0 &&
|
||||||
|
S_ISREG(st2.st_mode)) {
|
||||||
|
/* if its a symlink then ensure it doesn't
|
||||||
|
point at something called exclude_name */
|
||||||
|
if (S_ISLNK(st1.st_mode)) {
|
||||||
|
char *buf = x_realpath(fname);
|
||||||
|
if (buf) {
|
||||||
|
char *p = str_basename(buf);
|
||||||
|
if (strcmp(p, exclude_name) == 0) {
|
||||||
|
/* its a link to "ccache" ! */
|
||||||
|
free(p);
|
||||||
|
free(buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* found it! */
|
||||||
|
free(path);
|
||||||
|
return fname;
|
||||||
|
}
|
||||||
|
free(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
80
CCache/hash.c
Normal file
80
CCache/hash.c
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) Andrew Tridgell 2002
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
simple front-end functions to mdfour code
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ccache.h"
|
||||||
|
|
||||||
|
static struct mdfour md;
|
||||||
|
|
||||||
|
void hash_buffer(const char *s, int len)
|
||||||
|
{
|
||||||
|
mdfour_update(&md, (unsigned char *)s, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_start(void)
|
||||||
|
{
|
||||||
|
mdfour_begin(&md);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_string(const char *s)
|
||||||
|
{
|
||||||
|
hash_buffer(s, strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_int(int x)
|
||||||
|
{
|
||||||
|
hash_buffer((char *)&x, sizeof(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add contents of a file to the hash */
|
||||||
|
void hash_file(const char *fname)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int fd, n;
|
||||||
|
|
||||||
|
fd = open(fname, O_RDONLY|O_BINARY);
|
||||||
|
if (fd == -1) {
|
||||||
|
cc_log("Failed to open %s\n", fname);
|
||||||
|
fatal("hash_file");
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||||
|
hash_buffer(buf, n);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return the hash result as a static string */
|
||||||
|
char *hash_result(void)
|
||||||
|
{
|
||||||
|
unsigned char sum[16];
|
||||||
|
static char ret[53];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
hash_buffer(NULL, 0);
|
||||||
|
mdfour_result(&md, sum);
|
||||||
|
|
||||||
|
for (i=0;i<16;i++) {
|
||||||
|
sprintf(&ret[i*2], "%02x", (unsigned)sum[i]);
|
||||||
|
}
|
||||||
|
sprintf(&ret[i*2], "-%u", (unsigned)md.totalN);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
238
CCache/install-sh
Executable file
238
CCache/install-sh
Executable file
|
|
@ -0,0 +1,238 @@
|
||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# install - install a program, script, or datafile
|
||||||
|
# This comes from X11R5.
|
||||||
|
#
|
||||||
|
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||||
|
# `make' implicit rules from creating a file called install from it
|
||||||
|
# when there is no Makefile.
|
||||||
|
#
|
||||||
|
# This script is compatible with the BSD install script, but was written
|
||||||
|
# from scratch.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# set DOITPROG to echo to test this script
|
||||||
|
|
||||||
|
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||||
|
doit="${DOITPROG-}"
|
||||||
|
|
||||||
|
|
||||||
|
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||||
|
|
||||||
|
mvprog="${MVPROG-mv}"
|
||||||
|
cpprog="${CPPROG-cp}"
|
||||||
|
chmodprog="${CHMODPROG-chmod}"
|
||||||
|
chownprog="${CHOWNPROG-chown}"
|
||||||
|
chgrpprog="${CHGRPPROG-chgrp}"
|
||||||
|
stripprog="${STRIPPROG-strip}"
|
||||||
|
rmprog="${RMPROG-rm}"
|
||||||
|
mkdirprog="${MKDIRPROG-mkdir}"
|
||||||
|
|
||||||
|
transformbasename=""
|
||||||
|
transform_arg=""
|
||||||
|
instcmd="$mvprog"
|
||||||
|
chmodcmd="$chmodprog 0755"
|
||||||
|
chowncmd=""
|
||||||
|
chgrpcmd=""
|
||||||
|
stripcmd=""
|
||||||
|
rmcmd="$rmprog -f"
|
||||||
|
mvcmd="$mvprog"
|
||||||
|
src=""
|
||||||
|
dst=""
|
||||||
|
dir_arg=""
|
||||||
|
|
||||||
|
while [ x"$1" != x ]; do
|
||||||
|
case $1 in
|
||||||
|
-c) instcmd="$cpprog"
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
-d) dir_arg=true
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
-m) chmodcmd="$chmodprog $2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
-o) chowncmd="$chownprog $2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
-g) chgrpcmd="$chgrpprog $2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
-s) stripcmd="$stripprog"
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
|
||||||
|
*) if [ x"$src" = x ]
|
||||||
|
then
|
||||||
|
src=$1
|
||||||
|
else
|
||||||
|
# this colon is to work around a 386BSD /bin/sh bug
|
||||||
|
:
|
||||||
|
dst=$1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
continue;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ x"$src" = x ]
|
||||||
|
then
|
||||||
|
echo "install: no input file specified"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x"$dir_arg" != x ]; then
|
||||||
|
dst=$src
|
||||||
|
src=""
|
||||||
|
|
||||||
|
if [ -d $dst ]; then
|
||||||
|
instcmd=:
|
||||||
|
else
|
||||||
|
instcmd=mkdir
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
|
||||||
|
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||||
|
# might cause directories to be created, which would be especially bad
|
||||||
|
# if $src (and thus $dsttmp) contains '*'.
|
||||||
|
|
||||||
|
if [ -f $src -o -d $src ]
|
||||||
|
then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
echo "install: $src does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x"$dst" = x ]
|
||||||
|
then
|
||||||
|
echo "install: no destination specified"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If destination is a directory, append the input filename; if your system
|
||||||
|
# does not like double slashes in filenames, you may need to add some logic
|
||||||
|
|
||||||
|
if [ -d $dst ]
|
||||||
|
then
|
||||||
|
dst="$dst"/`basename $src`
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
## this sed command emulates the dirname command
|
||||||
|
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||||
|
|
||||||
|
# Make sure that the destination directory exists.
|
||||||
|
# this part is taken from Noah Friedman's mkinstalldirs script
|
||||||
|
|
||||||
|
# Skip lots of stat calls in the usual case.
|
||||||
|
if [ ! -d "$dstdir" ]; then
|
||||||
|
defaultIFS='
|
||||||
|
'
|
||||||
|
IFS="${IFS-${defaultIFS}}"
|
||||||
|
|
||||||
|
oIFS="${IFS}"
|
||||||
|
# Some sh's can't handle IFS=/ for some reason.
|
||||||
|
IFS='%'
|
||||||
|
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||||
|
IFS="${oIFS}"
|
||||||
|
|
||||||
|
pathcomp=''
|
||||||
|
|
||||||
|
while [ $# -ne 0 ] ; do
|
||||||
|
pathcomp="${pathcomp}${1}"
|
||||||
|
shift
|
||||||
|
|
||||||
|
if [ ! -d "${pathcomp}" ] ;
|
||||||
|
then
|
||||||
|
$mkdirprog "${pathcomp}"
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
pathcomp="${pathcomp}/"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x"$dir_arg" != x ]
|
||||||
|
then
|
||||||
|
$doit $instcmd $dst &&
|
||||||
|
|
||||||
|
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
||||||
|
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
||||||
|
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
||||||
|
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
||||||
|
else
|
||||||
|
|
||||||
|
# If we're going to rename the final executable, determine the name now.
|
||||||
|
|
||||||
|
if [ x"$transformarg" = x ]
|
||||||
|
then
|
||||||
|
dstfile=`basename $dst`
|
||||||
|
else
|
||||||
|
dstfile=`basename $dst $transformbasename |
|
||||||
|
sed $transformarg`$transformbasename
|
||||||
|
fi
|
||||||
|
|
||||||
|
# don't allow the sed command to completely eliminate the filename
|
||||||
|
|
||||||
|
if [ x"$dstfile" = x ]
|
||||||
|
then
|
||||||
|
dstfile=`basename $dst`
|
||||||
|
else
|
||||||
|
true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make a temp file name in the proper directory.
|
||||||
|
|
||||||
|
dsttmp=$dstdir/#inst.$$#
|
||||||
|
|
||||||
|
# Move or copy the file name to the temp name
|
||||||
|
|
||||||
|
$doit $instcmd $src $dsttmp &&
|
||||||
|
|
||||||
|
trap "rm -f ${dsttmp}" 0 &&
|
||||||
|
|
||||||
|
# and set any options; do chmod last to preserve setuid bits
|
||||||
|
|
||||||
|
# If any of these fail, we abort the whole thing. If we want to
|
||||||
|
# ignore errors from any of these, just make sure not to ignore
|
||||||
|
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
||||||
|
|
||||||
|
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
||||||
|
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
||||||
|
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
||||||
|
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
||||||
|
|
||||||
|
# Now rename the file to the real destination.
|
||||||
|
|
||||||
|
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||||
|
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||||
|
|
||||||
|
fi &&
|
||||||
|
|
||||||
|
|
||||||
|
exit 0
|
||||||
284
CCache/mdfour.c
Normal file
284
CCache/mdfour.c
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
/*
|
||||||
|
a implementation of MD4 designed for use in the SMB authentication protocol
|
||||||
|
Copyright (C) Andrew Tridgell 1997-1998.
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ccache.h"
|
||||||
|
|
||||||
|
/* NOTE: This code makes no attempt to be fast!
|
||||||
|
|
||||||
|
It assumes that a int is at least 32 bits long
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct mdfour *m;
|
||||||
|
|
||||||
|
#define MASK32 (0xffffffff)
|
||||||
|
|
||||||
|
#define F(X,Y,Z) ((((X)&(Y)) | ((~(X))&(Z))))
|
||||||
|
#define G(X,Y,Z) ((((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))))
|
||||||
|
#define H(X,Y,Z) (((X)^(Y)^(Z)))
|
||||||
|
#define lshift(x,s) (((((x)<<(s))&MASK32) | (((x)>>(32-(s)))&MASK32)))
|
||||||
|
|
||||||
|
#define ROUND1(a,b,c,d,k,s) a = lshift((a + F(b,c,d) + M[k])&MASK32, s)
|
||||||
|
#define ROUND2(a,b,c,d,k,s) a = lshift((a + G(b,c,d) + M[k] + 0x5A827999)&MASK32,s)
|
||||||
|
#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 */
|
||||||
|
static void mdfour64(uint32 *M)
|
||||||
|
{
|
||||||
|
uint32 AA, BB, CC, DD;
|
||||||
|
uint32 A,B,C,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);
|
||||||
|
ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
|
||||||
|
ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
|
||||||
|
ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
|
||||||
|
ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
|
||||||
|
ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
|
||||||
|
ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
|
||||||
|
|
||||||
|
|
||||||
|
ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
|
||||||
|
ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
|
||||||
|
ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
|
||||||
|
ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
|
||||||
|
ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
|
||||||
|
ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
|
||||||
|
ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
|
||||||
|
ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
|
||||||
|
|
||||||
|
ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
|
||||||
|
ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
|
||||||
|
ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
|
||||||
|
ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
|
||||||
|
ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
|
||||||
|
ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
|
||||||
|
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 &= MASK32; B &= MASK32;
|
||||||
|
C &= MASK32; D &= MASK32;
|
||||||
|
|
||||||
|
m->A = A; m->B = B; m->C = C; m->D = D;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy64(uint32 *M, const unsigned char *in)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy4(unsigned char *out,uint32 x)
|
||||||
|
{
|
||||||
|
out[0] = x&0xFF;
|
||||||
|
out[1] = (x>>8)&0xFF;
|
||||||
|
out[2] = (x>>16)&0xFF;
|
||||||
|
out[3] = (x>>24)&0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mdfour_begin(struct mdfour *md)
|
||||||
|
{
|
||||||
|
md->A = 0x67452301;
|
||||||
|
md->B = 0xefcdab89;
|
||||||
|
md->C = 0x98badcfe;
|
||||||
|
md->D = 0x10325476;
|
||||||
|
md->totalN = 0;
|
||||||
|
md->tail_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mdfour_tail(const unsigned char *in, int n)
|
||||||
|
{
|
||||||
|
unsigned char buf[128];
|
||||||
|
uint32 M[16];
|
||||||
|
uint32 b;
|
||||||
|
|
||||||
|
m->totalN += n;
|
||||||
|
|
||||||
|
b = m->totalN * 8;
|
||||||
|
|
||||||
|
memset(buf, 0, 128);
|
||||||
|
if (n) memcpy(buf, in, n);
|
||||||
|
buf[n] = 0x80;
|
||||||
|
|
||||||
|
if (n <= 55) {
|
||||||
|
copy4(buf+56, b);
|
||||||
|
copy64(M, buf);
|
||||||
|
mdfour64(M);
|
||||||
|
} else {
|
||||||
|
copy4(buf+120, b);
|
||||||
|
copy64(M, buf);
|
||||||
|
mdfour64(M);
|
||||||
|
copy64(M, buf+64);
|
||||||
|
mdfour64(M);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mdfour_update(struct mdfour *md, const unsigned char *in, int n)
|
||||||
|
{
|
||||||
|
uint32 M[16];
|
||||||
|
|
||||||
|
m = md;
|
||||||
|
|
||||||
|
if (in == NULL) {
|
||||||
|
mdfour_tail(md->tail, md->tail_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (md->tail_len) {
|
||||||
|
int len = 64 - md->tail_len;
|
||||||
|
if (len > n) len = n;
|
||||||
|
memcpy(md->tail+md->tail_len, in, len);
|
||||||
|
md->tail_len += len;
|
||||||
|
n -= len;
|
||||||
|
in += len;
|
||||||
|
if (md->tail_len == 64) {
|
||||||
|
copy64(M, md->tail);
|
||||||
|
mdfour64(M);
|
||||||
|
m->totalN += 64;
|
||||||
|
md->tail_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (n >= 64) {
|
||||||
|
copy64(M, in);
|
||||||
|
mdfour64(M);
|
||||||
|
in += 64;
|
||||||
|
n -= 64;
|
||||||
|
m->totalN += 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n) {
|
||||||
|
memcpy(md->tail, in, n);
|
||||||
|
md->tail_len = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mdfour_result(struct mdfour *md, unsigned char *out)
|
||||||
|
{
|
||||||
|
m = md;
|
||||||
|
|
||||||
|
copy4(out, m->A);
|
||||||
|
copy4(out+4, m->B);
|
||||||
|
copy4(out+8, m->C);
|
||||||
|
copy4(out+12, m->D);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mdfour(unsigned char *out, const unsigned char *in, int n)
|
||||||
|
{
|
||||||
|
struct mdfour md;
|
||||||
|
mdfour_begin(&md);
|
||||||
|
mdfour_update(&md, in, n);
|
||||||
|
mdfour_update(&md, NULL, 0);
|
||||||
|
mdfour_result(&md, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST_MDFOUR
|
||||||
|
static void file_checksum1(char *fname)
|
||||||
|
{
|
||||||
|
int fd, i;
|
||||||
|
struct mdfour md;
|
||||||
|
unsigned char buf[1024], sum[16];
|
||||||
|
unsigned chunk;
|
||||||
|
|
||||||
|
fd = open(fname,O_RDONLY|O_BINARY);
|
||||||
|
if (fd == -1) {
|
||||||
|
perror("fname");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = 1 + random() % (sizeof(buf) - 1);
|
||||||
|
|
||||||
|
mdfour_begin(&md);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int n = read(fd, buf, chunk);
|
||||||
|
if (n >= 0) {
|
||||||
|
mdfour_update(&md, buf, n);
|
||||||
|
}
|
||||||
|
if (n < chunk) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
mdfour_update(&md, NULL, 0);
|
||||||
|
|
||||||
|
mdfour_result(&md, sum);
|
||||||
|
|
||||||
|
for (i=0;i<16;i++)
|
||||||
|
printf("%02x", sum[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#include "../md4.h"
|
||||||
|
|
||||||
|
static void file_checksum2(char *fname)
|
||||||
|
{
|
||||||
|
int fd, i;
|
||||||
|
MDstruct md;
|
||||||
|
unsigned char buf[64], sum[16];
|
||||||
|
|
||||||
|
fd = open(fname,O_RDONLY|O_BINARY);
|
||||||
|
if (fd == -1) {
|
||||||
|
perror("fname");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDbegin(&md);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int n = read(fd, buf, sizeof(buf));
|
||||||
|
if (n <= 0) break;
|
||||||
|
MDupdate(&md, buf, n*8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!md.done) {
|
||||||
|
MDupdate(&md, buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
memcpy(sum, md.buffer, 16);
|
||||||
|
|
||||||
|
for (i=0;i<16;i++)
|
||||||
|
printf("%02x", sum[i]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
file_checksum1(argv[1]);
|
||||||
|
#if 0
|
||||||
|
file_checksum2(argv[1]);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
36
CCache/mdfour.h
Normal file
36
CCache/mdfour.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
Unix SMB/Netbios implementation.
|
||||||
|
Version 1.9.
|
||||||
|
a implementation of MD4 designed for use in the SMB authentication protocol
|
||||||
|
Copyright (C) Andrew Tridgell 1997-1998.
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct mdfour {
|
||||||
|
uint32 A, B, C, D;
|
||||||
|
uint32 totalN;
|
||||||
|
unsigned char tail[64];
|
||||||
|
unsigned tail_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mdfour_begin(struct mdfour *md);
|
||||||
|
void mdfour_update(struct mdfour *md, const unsigned char *in, int n);
|
||||||
|
void mdfour_result(struct mdfour *md, unsigned char *out);
|
||||||
|
void mdfour(unsigned char *out, const unsigned char *in, int n);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
5
CCache/packaging/README
Normal file
5
CCache/packaging/README
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
These packaging files are contributd by users of ccache. I do not
|
||||||
|
maintain them, and they may well need updating before you use them.
|
||||||
|
|
||||||
|
I don't distribute binary packages of ccache myself, but if you wish
|
||||||
|
to add ccache to a distribution then that's OK
|
||||||
37
CCache/packaging/ccache.spec
Normal file
37
CCache/packaging/ccache.spec
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
Summary: Compiler Cache
|
||||||
|
Name: ccache
|
||||||
|
Version: 2.3
|
||||||
|
Release: 1
|
||||||
|
Group: Development/Languages
|
||||||
|
License: GPL
|
||||||
|
URL: http://ccache.samba.org/
|
||||||
|
Source: ccache-%{version}.tar.gz
|
||||||
|
BuildRoot: %{_tmppath}/%{name}-%{version}-root
|
||||||
|
|
||||||
|
%description
|
||||||
|
ccache caches gcc output files
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q
|
||||||
|
|
||||||
|
%build
|
||||||
|
%configure
|
||||||
|
make
|
||||||
|
|
||||||
|
install -d -m 0755 $RPM_BUILD_ROOT%{_bindir}
|
||||||
|
install -m 0755 ccache $RPM_BUILD_ROOT%{_bindir}
|
||||||
|
install -d -m 0755 $RPM_BUILD_ROOT%{_mandir}/man1
|
||||||
|
install -m 0644 ccache.1 $RPM_BUILD_ROOT%{_mandir}/man1
|
||||||
|
|
||||||
|
%files
|
||||||
|
%defattr(-,root,root)
|
||||||
|
%doc README
|
||||||
|
%{_mandir}/man1/ccache.1*
|
||||||
|
%{_bindir}/ccache
|
||||||
|
|
||||||
|
%clean
|
||||||
|
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Mon Apr 01 2002 Peter Jones <pjones@redhat.com>
|
||||||
|
- Created the package
|
||||||
962
CCache/snprintf.c
Normal file
962
CCache/snprintf.c
Normal file
|
|
@ -0,0 +1,962 @@
|
||||||
|
/*
|
||||||
|
* Copyright Patrick Powell 1995
|
||||||
|
* This code is based on code written by Patrick Powell (papowell@astart.com)
|
||||||
|
* It may be used for any purpose as long as this notice remains intact
|
||||||
|
* on all source code distributions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**************************************************************
|
||||||
|
* Original:
|
||||||
|
* Patrick Powell Tue Apr 11 09:48:21 PDT 1995
|
||||||
|
* A bombproof version of doprnt (dopr) included.
|
||||||
|
* Sigh. This sort of thing is always nasty do deal with. Note that
|
||||||
|
* the version here does not include floating point...
|
||||||
|
*
|
||||||
|
* snprintf() is used instead of sprintf() as it does limit checks
|
||||||
|
* for string length. This covers a nasty loophole.
|
||||||
|
*
|
||||||
|
* The other functions are there to prevent NULL pointers from
|
||||||
|
* causing nast effects.
|
||||||
|
*
|
||||||
|
* More Recently:
|
||||||
|
* Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
|
||||||
|
* This was ugly. It is still ugly. I opted out of floating point
|
||||||
|
* numbers, but the formatter understands just about everything
|
||||||
|
* from the normal C string format, at least as far as I can tell from
|
||||||
|
* the Solaris 2.5 printf(3S) man page.
|
||||||
|
*
|
||||||
|
* Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
|
||||||
|
* Ok, added some minimal floating point support, which means this
|
||||||
|
* probably requires libm on most operating systems. Don't yet
|
||||||
|
* support the exponent (e,E) and sigfig (g,G). Also, fmtint()
|
||||||
|
* was pretty badly broken, it just wasn't being exercised in ways
|
||||||
|
* which showed it, so that's been fixed. Also, formated the code
|
||||||
|
* to mutt conventions, and removed dead code left over from the
|
||||||
|
* original. Also, there is now a builtin-test, just compile with:
|
||||||
|
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
|
||||||
|
* and run snprintf for results.
|
||||||
|
*
|
||||||
|
* Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
|
||||||
|
* The PGP code was using unsigned hexadecimal formats.
|
||||||
|
* Unfortunately, unsigned formats simply didn't work.
|
||||||
|
*
|
||||||
|
* Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
|
||||||
|
* The original code assumed that both snprintf() and vsnprintf() were
|
||||||
|
* missing. Some systems only have snprintf() but not vsnprintf(), so
|
||||||
|
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
|
||||||
|
*
|
||||||
|
* Andrew Tridgell (tridge@samba.org) Oct 1998
|
||||||
|
* fixed handling of %.0f
|
||||||
|
* added test for HAVE_LONG_DOUBLE
|
||||||
|
*
|
||||||
|
* tridge@samba.org, idra@samba.org, April 2001
|
||||||
|
* got rid of fcvt code (twas buggy and made testing harder)
|
||||||
|
* added C99 semantics
|
||||||
|
*
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
#ifndef NO_CONFIG_H /* for some tests */
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STRINGS_H
|
||||||
|
#include <strings.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_CTYPE_H
|
||||||
|
#include <ctype.h>
|
||||||
|
#endif
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
|
||||||
|
/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
|
||||||
|
#include <stdio.h>
|
||||||
|
/* make the compiler happy with an empty file */
|
||||||
|
void dummy_snprintf(void) {}
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef HAVE_LONG_DOUBLE
|
||||||
|
#define LDOUBLE long double
|
||||||
|
#else
|
||||||
|
#define LDOUBLE double
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LONG_LONG
|
||||||
|
#define LLONG long long
|
||||||
|
#else
|
||||||
|
#define LLONG long
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static size_t dopr(char *buffer, size_t maxlen, const char *format,
|
||||||
|
va_list args);
|
||||||
|
static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
|
||||||
|
char *value, int flags, int min, int max);
|
||||||
|
static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
|
||||||
|
long value, int base, int min, int max, int flags);
|
||||||
|
static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
|
||||||
|
LDOUBLE fvalue, int min, int max, int flags);
|
||||||
|
static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dopr(): poor man's version of doprintf
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* format read states */
|
||||||
|
#define DP_S_DEFAULT 0
|
||||||
|
#define DP_S_FLAGS 1
|
||||||
|
#define DP_S_MIN 2
|
||||||
|
#define DP_S_DOT 3
|
||||||
|
#define DP_S_MAX 4
|
||||||
|
#define DP_S_MOD 5
|
||||||
|
#define DP_S_CONV 6
|
||||||
|
#define DP_S_DONE 7
|
||||||
|
|
||||||
|
/* format flags - Bits */
|
||||||
|
#define DP_F_MINUS (1 << 0)
|
||||||
|
#define DP_F_PLUS (1 << 1)
|
||||||
|
#define DP_F_SPACE (1 << 2)
|
||||||
|
#define DP_F_NUM (1 << 3)
|
||||||
|
#define DP_F_ZERO (1 << 4)
|
||||||
|
#define DP_F_UP (1 << 5)
|
||||||
|
#define DP_F_UNSIGNED (1 << 6)
|
||||||
|
|
||||||
|
/* Conversion Flags */
|
||||||
|
#define DP_C_SHORT 1
|
||||||
|
#define DP_C_LONG 2
|
||||||
|
#define DP_C_LDOUBLE 3
|
||||||
|
#define DP_C_LLONG 4
|
||||||
|
|
||||||
|
#define char_to_int(p) ((p)- '0')
|
||||||
|
#ifndef MAX
|
||||||
|
#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
LLONG value;
|
||||||
|
LDOUBLE fvalue;
|
||||||
|
char *strvalue;
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
int state;
|
||||||
|
int flags;
|
||||||
|
int cflags;
|
||||||
|
size_t currlen;
|
||||||
|
|
||||||
|
state = DP_S_DEFAULT;
|
||||||
|
currlen = flags = cflags = min = 0;
|
||||||
|
max = -1;
|
||||||
|
ch = *format++;
|
||||||
|
|
||||||
|
while (state != DP_S_DONE) {
|
||||||
|
if (ch == '\0')
|
||||||
|
state = DP_S_DONE;
|
||||||
|
|
||||||
|
switch(state) {
|
||||||
|
case DP_S_DEFAULT:
|
||||||
|
if (ch == '%')
|
||||||
|
state = DP_S_FLAGS;
|
||||||
|
else
|
||||||
|
dopr_outch (buffer, &currlen, maxlen, ch);
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
case DP_S_FLAGS:
|
||||||
|
switch (ch) {
|
||||||
|
case '-':
|
||||||
|
flags |= DP_F_MINUS;
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
flags |= DP_F_PLUS;
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
flags |= DP_F_SPACE;
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
flags |= DP_F_NUM;
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
flags |= DP_F_ZERO;
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = DP_S_MIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DP_S_MIN:
|
||||||
|
if (isdigit((unsigned char)ch)) {
|
||||||
|
min = 10*min + char_to_int (ch);
|
||||||
|
ch = *format++;
|
||||||
|
} else if (ch == '*') {
|
||||||
|
min = va_arg (args, int);
|
||||||
|
ch = *format++;
|
||||||
|
state = DP_S_DOT;
|
||||||
|
} else {
|
||||||
|
state = DP_S_DOT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DP_S_DOT:
|
||||||
|
if (ch == '.') {
|
||||||
|
state = DP_S_MAX;
|
||||||
|
ch = *format++;
|
||||||
|
} else {
|
||||||
|
state = DP_S_MOD;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DP_S_MAX:
|
||||||
|
if (isdigit((unsigned char)ch)) {
|
||||||
|
if (max < 0)
|
||||||
|
max = 0;
|
||||||
|
max = 10*max + char_to_int (ch);
|
||||||
|
ch = *format++;
|
||||||
|
} else if (ch == '*') {
|
||||||
|
max = va_arg (args, int);
|
||||||
|
ch = *format++;
|
||||||
|
state = DP_S_MOD;
|
||||||
|
} else {
|
||||||
|
state = DP_S_MOD;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DP_S_MOD:
|
||||||
|
switch (ch) {
|
||||||
|
case 'h':
|
||||||
|
cflags = DP_C_SHORT;
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
cflags = DP_C_LONG;
|
||||||
|
ch = *format++;
|
||||||
|
if (ch == 'l') { /* It's a long long */
|
||||||
|
cflags = DP_C_LLONG;
|
||||||
|
ch = *format++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
cflags = DP_C_LDOUBLE;
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = DP_S_CONV;
|
||||||
|
break;
|
||||||
|
case DP_S_CONV:
|
||||||
|
switch (ch) {
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
if (cflags == DP_C_SHORT)
|
||||||
|
value = va_arg (args, int);
|
||||||
|
else if (cflags == DP_C_LONG)
|
||||||
|
value = va_arg (args, long int);
|
||||||
|
else if (cflags == DP_C_LLONG)
|
||||||
|
value = va_arg (args, LLONG);
|
||||||
|
else
|
||||||
|
value = va_arg (args, int);
|
||||||
|
fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
flags |= DP_F_UNSIGNED;
|
||||||
|
if (cflags == DP_C_SHORT)
|
||||||
|
value = va_arg (args, unsigned int);
|
||||||
|
else if (cflags == DP_C_LONG)
|
||||||
|
value = (long)va_arg (args, unsigned long int);
|
||||||
|
else if (cflags == DP_C_LLONG)
|
||||||
|
value = (long)va_arg (args, unsigned LLONG);
|
||||||
|
else
|
||||||
|
value = (long)va_arg (args, unsigned int);
|
||||||
|
fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
flags |= DP_F_UNSIGNED;
|
||||||
|
if (cflags == DP_C_SHORT)
|
||||||
|
value = va_arg (args, unsigned int);
|
||||||
|
else if (cflags == DP_C_LONG)
|
||||||
|
value = (long)va_arg (args, unsigned long int);
|
||||||
|
else if (cflags == DP_C_LLONG)
|
||||||
|
value = (LLONG)va_arg (args, unsigned LLONG);
|
||||||
|
else
|
||||||
|
value = (long)va_arg (args, unsigned int);
|
||||||
|
fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
flags |= DP_F_UP;
|
||||||
|
case 'x':
|
||||||
|
flags |= DP_F_UNSIGNED;
|
||||||
|
if (cflags == DP_C_SHORT)
|
||||||
|
value = va_arg (args, unsigned int);
|
||||||
|
else if (cflags == DP_C_LONG)
|
||||||
|
value = (long)va_arg (args, unsigned long int);
|
||||||
|
else if (cflags == DP_C_LLONG)
|
||||||
|
value = (LLONG)va_arg (args, unsigned LLONG);
|
||||||
|
else
|
||||||
|
value = (long)va_arg (args, unsigned int);
|
||||||
|
fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (cflags == DP_C_LDOUBLE)
|
||||||
|
fvalue = va_arg (args, LDOUBLE);
|
||||||
|
else
|
||||||
|
fvalue = va_arg (args, double);
|
||||||
|
/* um, floating point? */
|
||||||
|
fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
flags |= DP_F_UP;
|
||||||
|
case 'e':
|
||||||
|
if (cflags == DP_C_LDOUBLE)
|
||||||
|
fvalue = va_arg (args, LDOUBLE);
|
||||||
|
else
|
||||||
|
fvalue = va_arg (args, double);
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
flags |= DP_F_UP;
|
||||||
|
case 'g':
|
||||||
|
if (cflags == DP_C_LDOUBLE)
|
||||||
|
fvalue = va_arg (args, LDOUBLE);
|
||||||
|
else
|
||||||
|
fvalue = va_arg (args, double);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
strvalue = va_arg (args, char *);
|
||||||
|
if (!strvalue) strvalue = "(NULL)";
|
||||||
|
if (max == -1) {
|
||||||
|
max = strlen(strvalue);
|
||||||
|
}
|
||||||
|
if (min > 0 && max >= 0 && min > max) max = min;
|
||||||
|
fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
strvalue = (char *)va_arg(args, void *);
|
||||||
|
fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
if (cflags == DP_C_SHORT) {
|
||||||
|
short int *num;
|
||||||
|
num = va_arg (args, short int *);
|
||||||
|
*num = currlen;
|
||||||
|
} else if (cflags == DP_C_LONG) {
|
||||||
|
long int *num;
|
||||||
|
num = va_arg (args, long int *);
|
||||||
|
*num = (long int)currlen;
|
||||||
|
} else if (cflags == DP_C_LLONG) {
|
||||||
|
LLONG *num;
|
||||||
|
num = va_arg (args, LLONG *);
|
||||||
|
*num = (LLONG)currlen;
|
||||||
|
} else {
|
||||||
|
int *num;
|
||||||
|
num = va_arg (args, int *);
|
||||||
|
*num = currlen;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
dopr_outch (buffer, &currlen, maxlen, ch);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
/* not supported yet, treat as next char */
|
||||||
|
ch = *format++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Unknown, skip */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ch = *format++;
|
||||||
|
state = DP_S_DEFAULT;
|
||||||
|
flags = cflags = min = 0;
|
||||||
|
max = -1;
|
||||||
|
break;
|
||||||
|
case DP_S_DONE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* hmm? */
|
||||||
|
break; /* some picky compilers need this */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxlen != 0) {
|
||||||
|
if (currlen < maxlen - 1)
|
||||||
|
buffer[currlen] = '\0';
|
||||||
|
else if (maxlen > 0)
|
||||||
|
buffer[maxlen - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return currlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
|
||||||
|
char *value, int flags, int min, int max)
|
||||||
|
{
|
||||||
|
int padlen, strln; /* amount to pad */
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_SNPRINTF
|
||||||
|
printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
|
||||||
|
#endif
|
||||||
|
if (value == 0) {
|
||||||
|
value = "<NULL>";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (strln = 0; value[strln]; ++strln); /* strlen */
|
||||||
|
padlen = min - strln;
|
||||||
|
if (padlen < 0)
|
||||||
|
padlen = 0;
|
||||||
|
if (flags & DP_F_MINUS)
|
||||||
|
padlen = -padlen; /* Left Justify */
|
||||||
|
|
||||||
|
while ((padlen > 0) && (cnt < max)) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, ' ');
|
||||||
|
--padlen;
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
while (*value && (cnt < max)) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, *value++);
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
while ((padlen < 0) && (cnt < max)) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, ' ');
|
||||||
|
++padlen;
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
|
||||||
|
|
||||||
|
static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
|
||||||
|
long value, int base, int min, int max, int flags)
|
||||||
|
{
|
||||||
|
int signvalue = 0;
|
||||||
|
unsigned long uvalue;
|
||||||
|
char convert[20];
|
||||||
|
int place = 0;
|
||||||
|
int spadlen = 0; /* amount to space pad */
|
||||||
|
int zpadlen = 0; /* amount to zero pad */
|
||||||
|
int caps = 0;
|
||||||
|
|
||||||
|
if (max < 0)
|
||||||
|
max = 0;
|
||||||
|
|
||||||
|
uvalue = value;
|
||||||
|
|
||||||
|
if(!(flags & DP_F_UNSIGNED)) {
|
||||||
|
if( value < 0 ) {
|
||||||
|
signvalue = '-';
|
||||||
|
uvalue = -value;
|
||||||
|
} else {
|
||||||
|
if (flags & DP_F_PLUS) /* Do a sign (+/i) */
|
||||||
|
signvalue = '+';
|
||||||
|
else if (flags & DP_F_SPACE)
|
||||||
|
signvalue = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
|
||||||
|
|
||||||
|
do {
|
||||||
|
convert[place++] =
|
||||||
|
(caps? "0123456789ABCDEF":"0123456789abcdef")
|
||||||
|
[uvalue % (unsigned)base ];
|
||||||
|
uvalue = (uvalue / (unsigned)base );
|
||||||
|
} while(uvalue && (place < 20));
|
||||||
|
if (place == 20) place--;
|
||||||
|
convert[place] = 0;
|
||||||
|
|
||||||
|
zpadlen = max - place;
|
||||||
|
spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
|
||||||
|
if (zpadlen < 0) zpadlen = 0;
|
||||||
|
if (spadlen < 0) spadlen = 0;
|
||||||
|
if (flags & DP_F_ZERO) {
|
||||||
|
zpadlen = MAX(zpadlen, spadlen);
|
||||||
|
spadlen = 0;
|
||||||
|
}
|
||||||
|
if (flags & DP_F_MINUS)
|
||||||
|
spadlen = -spadlen; /* Left Justifty */
|
||||||
|
|
||||||
|
#ifdef DEBUG_SNPRINTF
|
||||||
|
printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
|
||||||
|
zpadlen, spadlen, min, max, place);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Spaces */
|
||||||
|
while (spadlen > 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, ' ');
|
||||||
|
--spadlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sign */
|
||||||
|
if (signvalue)
|
||||||
|
dopr_outch (buffer, currlen, maxlen, signvalue);
|
||||||
|
|
||||||
|
/* Zeros */
|
||||||
|
if (zpadlen > 0) {
|
||||||
|
while (zpadlen > 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, '0');
|
||||||
|
--zpadlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Digits */
|
||||||
|
while (place > 0)
|
||||||
|
dopr_outch (buffer, currlen, maxlen, convert[--place]);
|
||||||
|
|
||||||
|
/* Left Justified spaces */
|
||||||
|
while (spadlen < 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, ' ');
|
||||||
|
++spadlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LDOUBLE abs_val(LDOUBLE value)
|
||||||
|
{
|
||||||
|
LDOUBLE result = value;
|
||||||
|
|
||||||
|
if (value < 0)
|
||||||
|
result = -value;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LDOUBLE POW10(int exp)
|
||||||
|
{
|
||||||
|
LDOUBLE result = 1;
|
||||||
|
|
||||||
|
while (exp) {
|
||||||
|
result *= 10;
|
||||||
|
exp--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLONG ROUND(LDOUBLE value)
|
||||||
|
{
|
||||||
|
LLONG intpart;
|
||||||
|
|
||||||
|
intpart = (LLONG)value;
|
||||||
|
value = value - intpart;
|
||||||
|
if (value >= 0.5) intpart++;
|
||||||
|
|
||||||
|
return intpart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a replacement for modf that doesn't need the math library. Should
|
||||||
|
be portable, but slow */
|
||||||
|
static double my_modf(double x0, double *iptr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
long l;
|
||||||
|
double x = x0;
|
||||||
|
double f = 1.0;
|
||||||
|
|
||||||
|
for (i=0;i<100;i++) {
|
||||||
|
l = (long)x;
|
||||||
|
if (l <= (x+1) && l >= (x-1)) break;
|
||||||
|
x *= 0.1;
|
||||||
|
f *= 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 100) {
|
||||||
|
/* yikes! the number is beyond what we can handle. What do we do? */
|
||||||
|
(*iptr) = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i != 0) {
|
||||||
|
double i2;
|
||||||
|
double ret;
|
||||||
|
|
||||||
|
ret = my_modf(x0-l*f, &i2);
|
||||||
|
(*iptr) = l*f + i2;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*iptr) = l;
|
||||||
|
return x - (*iptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
|
||||||
|
LDOUBLE fvalue, int min, int max, int flags)
|
||||||
|
{
|
||||||
|
int signvalue = 0;
|
||||||
|
double ufvalue;
|
||||||
|
char iconvert[311];
|
||||||
|
char fconvert[311];
|
||||||
|
int iplace = 0;
|
||||||
|
int fplace = 0;
|
||||||
|
int padlen = 0; /* amount to pad */
|
||||||
|
int zpadlen = 0;
|
||||||
|
int caps = 0;
|
||||||
|
int index;
|
||||||
|
double intpart;
|
||||||
|
double fracpart;
|
||||||
|
double temp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AIX manpage says the default is 0, but Solaris says the default
|
||||||
|
* is 6, and sprintf on AIX defaults to 6
|
||||||
|
*/
|
||||||
|
if (max < 0)
|
||||||
|
max = 6;
|
||||||
|
|
||||||
|
ufvalue = abs_val (fvalue);
|
||||||
|
|
||||||
|
if (fvalue < 0) {
|
||||||
|
signvalue = '-';
|
||||||
|
} else {
|
||||||
|
if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
|
||||||
|
signvalue = '+';
|
||||||
|
} else {
|
||||||
|
if (flags & DP_F_SPACE)
|
||||||
|
signvalue = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sorry, we only support 16 digits past the decimal because of our
|
||||||
|
* conversion method
|
||||||
|
*/
|
||||||
|
if (max > 16)
|
||||||
|
max = 16;
|
||||||
|
|
||||||
|
/* We "cheat" by converting the fractional part to integer by
|
||||||
|
* multiplying by a factor of 10
|
||||||
|
*/
|
||||||
|
|
||||||
|
temp = ufvalue;
|
||||||
|
my_modf(temp, &intpart);
|
||||||
|
|
||||||
|
fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
|
||||||
|
|
||||||
|
if (fracpart >= POW10(max)) {
|
||||||
|
intpart++;
|
||||||
|
fracpart -= POW10(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Convert integer part */
|
||||||
|
do {
|
||||||
|
temp = intpart;
|
||||||
|
my_modf(intpart*0.1, &intpart);
|
||||||
|
temp = temp*0.1;
|
||||||
|
index = (int) ((temp -intpart +0.05)* 10.0);
|
||||||
|
/* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
|
||||||
|
/* printf ("%llf, %f, %x\n", temp, intpart, index); */
|
||||||
|
iconvert[iplace++] =
|
||||||
|
(caps? "0123456789ABCDEF":"0123456789abcdef")[index];
|
||||||
|
} while (intpart && (iplace < 311));
|
||||||
|
if (iplace == 311) iplace--;
|
||||||
|
iconvert[iplace] = 0;
|
||||||
|
|
||||||
|
/* Convert fractional part */
|
||||||
|
if (fracpart)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
temp = fracpart;
|
||||||
|
my_modf(fracpart*0.1, &fracpart);
|
||||||
|
temp = temp*0.1;
|
||||||
|
index = (int) ((temp -fracpart +0.05)* 10.0);
|
||||||
|
/* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
|
||||||
|
/* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
|
||||||
|
fconvert[fplace++] =
|
||||||
|
(caps? "0123456789ABCDEF":"0123456789abcdef")[index];
|
||||||
|
} while(fracpart && (fplace < 311));
|
||||||
|
if (fplace == 311) fplace--;
|
||||||
|
}
|
||||||
|
fconvert[fplace] = 0;
|
||||||
|
|
||||||
|
/* -1 for decimal point, another -1 if we are printing a sign */
|
||||||
|
padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
|
||||||
|
zpadlen = max - fplace;
|
||||||
|
if (zpadlen < 0) zpadlen = 0;
|
||||||
|
if (padlen < 0)
|
||||||
|
padlen = 0;
|
||||||
|
if (flags & DP_F_MINUS)
|
||||||
|
padlen = -padlen; /* Left Justifty */
|
||||||
|
|
||||||
|
if ((flags & DP_F_ZERO) && (padlen > 0)) {
|
||||||
|
if (signvalue) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, signvalue);
|
||||||
|
--padlen;
|
||||||
|
signvalue = 0;
|
||||||
|
}
|
||||||
|
while (padlen > 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, '0');
|
||||||
|
--padlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (padlen > 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, ' ');
|
||||||
|
--padlen;
|
||||||
|
}
|
||||||
|
if (signvalue)
|
||||||
|
dopr_outch (buffer, currlen, maxlen, signvalue);
|
||||||
|
|
||||||
|
while (iplace > 0)
|
||||||
|
dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
|
||||||
|
|
||||||
|
#ifdef DEBUG_SNPRINTF
|
||||||
|
printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decimal point. This should probably use locale to find the correct
|
||||||
|
* char to print out.
|
||||||
|
*/
|
||||||
|
if (max > 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, '.');
|
||||||
|
|
||||||
|
while (fplace > 0)
|
||||||
|
dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (zpadlen > 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, '0');
|
||||||
|
--zpadlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (padlen < 0) {
|
||||||
|
dopr_outch (buffer, currlen, maxlen, ' ');
|
||||||
|
++padlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
|
||||||
|
{
|
||||||
|
if (*currlen < maxlen) {
|
||||||
|
buffer[(*currlen)] = c;
|
||||||
|
}
|
||||||
|
(*currlen)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* yes this really must be a ||. Don't muck with this (tridge) */
|
||||||
|
#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
|
||||||
|
int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
return dopr(str, count, fmt, args);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* yes this really must be a ||. Don't muck wiith this (tridge)
|
||||||
|
*
|
||||||
|
* The logic for these two is that we need our own definition if the
|
||||||
|
* OS *either* has no definition of *sprintf, or if it does have one
|
||||||
|
* that doesn't work properly according to the autoconf test. Perhaps
|
||||||
|
* these should really be smb_snprintf to avoid conflicts with buggy
|
||||||
|
* linkers? -- mbp
|
||||||
|
*/
|
||||||
|
#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_SNPRINTF)
|
||||||
|
int snprintf(char *str,size_t count,const char *fmt,...)
|
||||||
|
{
|
||||||
|
size_t ret;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = vsnprintf(str, count, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_VASPRINTF
|
||||||
|
int vasprintf(char **ptr, const char *format, va_list ap)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = vsnprintf(0, 0, format, ap);
|
||||||
|
if (ret <= 0) return ret;
|
||||||
|
|
||||||
|
(*ptr) = (char *)malloc(ret+1);
|
||||||
|
if (!*ptr) return -1;
|
||||||
|
ret = vsnprintf(*ptr, ret+1, format, ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HAVE_ASPRINTF
|
||||||
|
int asprintf(char **ptr, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
*ptr = 0;
|
||||||
|
va_start(ap, format);
|
||||||
|
ret = vasprintf(ptr, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_VSYSLOG
|
||||||
|
#ifdef HAVE_SYSLOG
|
||||||
|
void vsyslog (int facility_priority, char *format, va_list arglist)
|
||||||
|
{
|
||||||
|
char *msg = 0;
|
||||||
|
vasprintf(&msg, format, arglist);
|
||||||
|
if (!msg)
|
||||||
|
return;
|
||||||
|
syslog(facility_priority, "%s", msg);
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
#endif /* HAVE_SYSLOG */
|
||||||
|
#endif /* HAVE_VSYSLOG */
|
||||||
|
|
||||||
|
#ifdef TEST_SNPRINTF
|
||||||
|
|
||||||
|
int sprintf(char *str,const char *fmt,...);
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
char buf1[1024];
|
||||||
|
char buf2[1024];
|
||||||
|
char *fp_fmt[] = {
|
||||||
|
"%1.1f",
|
||||||
|
"%-1.5f",
|
||||||
|
"%1.5f",
|
||||||
|
"%123.9f",
|
||||||
|
"%10.5f",
|
||||||
|
"% 10.5f",
|
||||||
|
"%+22.9f",
|
||||||
|
"%+4.9f",
|
||||||
|
"%01.3f",
|
||||||
|
"%4f",
|
||||||
|
"%3.1f",
|
||||||
|
"%3.2f",
|
||||||
|
"%.0f",
|
||||||
|
"%f",
|
||||||
|
"-16.16f",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
|
||||||
|
0.9996, 1.996, 4.136, 0};
|
||||||
|
char *int_fmt[] = {
|
||||||
|
"%-1.5d",
|
||||||
|
"%1.5d",
|
||||||
|
"%123.9d",
|
||||||
|
"%5.5d",
|
||||||
|
"%10.5d",
|
||||||
|
"% 10.5d",
|
||||||
|
"%+22.33d",
|
||||||
|
"%01.3d",
|
||||||
|
"%4d",
|
||||||
|
"%d",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
|
||||||
|
char *str_fmt[] = {
|
||||||
|
"10.5s",
|
||||||
|
"5.10s",
|
||||||
|
"10.1s",
|
||||||
|
"0.10s",
|
||||||
|
"10.0s",
|
||||||
|
"1.10s",
|
||||||
|
"%s",
|
||||||
|
"%.1s",
|
||||||
|
"%.10s",
|
||||||
|
"%10s",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
char *str_vals[] = {"hello", "a", "", "a longer string", 0};
|
||||||
|
int x, y;
|
||||||
|
int fail = 0;
|
||||||
|
int num = 0;
|
||||||
|
|
||||||
|
printf ("Testing snprintf format codes against system sprintf...\n");
|
||||||
|
|
||||||
|
for (x = 0; fp_fmt[x] ; x++) {
|
||||||
|
for (y = 0; fp_nums[y] != 0 ; y++) {
|
||||||
|
int l1 = snprintf(0, 0, fp_fmt[x], fp_nums[y]);
|
||||||
|
int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
|
||||||
|
sprintf (buf2, fp_fmt[x], fp_nums[y]);
|
||||||
|
if (strcmp (buf1, buf2)) {
|
||||||
|
printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
|
||||||
|
fp_fmt[x], buf1, buf2);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
if (l1 != l2) {
|
||||||
|
printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; int_fmt[x] ; x++) {
|
||||||
|
for (y = 0; int_nums[y] != 0 ; y++) {
|
||||||
|
int l1 = snprintf(0, 0, int_fmt[x], int_nums[y]);
|
||||||
|
int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
|
||||||
|
sprintf (buf2, int_fmt[x], int_nums[y]);
|
||||||
|
if (strcmp (buf1, buf2)) {
|
||||||
|
printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
|
||||||
|
int_fmt[x], buf1, buf2);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
if (l1 != l2) {
|
||||||
|
printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; str_fmt[x] ; x++) {
|
||||||
|
for (y = 0; str_vals[y] != 0 ; y++) {
|
||||||
|
int l1 = snprintf(0, 0, str_fmt[x], str_vals[y]);
|
||||||
|
int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
|
||||||
|
sprintf (buf2, str_fmt[x], str_vals[y]);
|
||||||
|
if (strcmp (buf1, buf2)) {
|
||||||
|
printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
|
||||||
|
str_fmt[x], buf1, buf2);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
if (l1 != l2) {
|
||||||
|
printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf ("%d tests failed out of %d.\n", fail, num);
|
||||||
|
|
||||||
|
printf("seeing how many digits we support\n");
|
||||||
|
{
|
||||||
|
double v0 = 0.12345678901234567890123456789012345678901;
|
||||||
|
for (x=0; x<100; x++) {
|
||||||
|
snprintf(buf1, sizeof(buf1), "%1.1f", v0*pow(10, x));
|
||||||
|
sprintf(buf2, "%1.1f", v0*pow(10, x));
|
||||||
|
if (strcmp(buf1, buf2)) {
|
||||||
|
printf("we seem to support %d digits\n", x-1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* SNPRINTF_TEST */
|
||||||
354
CCache/stats.c
Normal file
354
CCache/stats.c
Normal file
|
|
@ -0,0 +1,354 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) Andrew Tridgell 2002
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
routines to handle the stats files
|
||||||
|
|
||||||
|
the stats file is stored one per cache subdirectory to make this more
|
||||||
|
scalable
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ccache.h"
|
||||||
|
|
||||||
|
extern char *stats_file;
|
||||||
|
extern char *cache_dir;
|
||||||
|
|
||||||
|
#define STATS_VERSION 1
|
||||||
|
|
||||||
|
#define FLAG_NOZERO 1 /* don't zero with the -z option */
|
||||||
|
#define FLAG_ALWAYS 2 /* always show, even if zero */
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
enum stats stat;
|
||||||
|
char *message;
|
||||||
|
void (*fn)(unsigned );
|
||||||
|
unsigned flags;
|
||||||
|
} stats_info[] = {
|
||||||
|
{ STATS_CACHED, "cache hit ", NULL, FLAG_ALWAYS },
|
||||||
|
{ STATS_TOCACHE, "cache miss ", NULL, FLAG_ALWAYS },
|
||||||
|
{ STATS_LINK, "called for link ", NULL, 0 },
|
||||||
|
{ STATS_MULTIPLE, "multiple source files ", NULL, 0 },
|
||||||
|
{ STATS_STDOUT, "compiler produced stdout ", NULL, 0 },
|
||||||
|
{ STATS_STATUS, "compile failed ", NULL, 0 },
|
||||||
|
{ STATS_ERROR, "ccache internal error ", NULL, 0 },
|
||||||
|
{ STATS_PREPROCESSOR, "preprocessor error ", NULL, 0 },
|
||||||
|
{ STATS_COMPILER, "couldn't find the compiler ", NULL, 0 },
|
||||||
|
{ STATS_MISSING, "cache file missing ", NULL, 0 },
|
||||||
|
{ STATS_ARGS, "bad compiler arguments ", NULL, 0 },
|
||||||
|
{ STATS_NOTC, "not a C/C++ file ", NULL, 0 },
|
||||||
|
{ STATS_CONFTEST, "autoconf compile/link ", NULL, 0 },
|
||||||
|
{ STATS_UNSUPPORTED, "unsupported compiler option ", NULL, 0 },
|
||||||
|
{ STATS_OUTSTDOUT, "output to stdout ", NULL, 0 },
|
||||||
|
{ STATS_DEVICE, "output to a non-regular file ", NULL, 0 },
|
||||||
|
{ STATS_NOINPUT, "no input file ", NULL, 0 },
|
||||||
|
{ STATS_NUMFILES, "files in cache ", NULL, FLAG_NOZERO|FLAG_ALWAYS },
|
||||||
|
{ STATS_TOTALSIZE, "cache size ", display_size , FLAG_NOZERO|FLAG_ALWAYS },
|
||||||
|
{ STATS_MAXFILES, "max files ", NULL, FLAG_NOZERO },
|
||||||
|
{ STATS_MAXSIZE, "max cache size ", display_size, FLAG_NOZERO },
|
||||||
|
{ STATS_NONE, NULL, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* parse a stats file from a buffer - adding to the counters */
|
||||||
|
static void parse_stats(unsigned counters[STATS_END], char *buf)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *p, *p2;
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
for (i=0;i<STATS_END;i++) {
|
||||||
|
counters[i] += strtol(p, &p2, 10);
|
||||||
|
if (!p2 || p2 == p) break;
|
||||||
|
p = p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write out a stats file */
|
||||||
|
static void write_stats(int fd, unsigned counters[STATS_END])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int len = 0;
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
|
for (i=0;i<STATS_END;i++) {
|
||||||
|
len += snprintf(buf+len, sizeof(buf)-(len+1), "%u ", counters[i]);
|
||||||
|
if (len >= (int)sizeof(buf)-1) fatal("stats too long?!");
|
||||||
|
}
|
||||||
|
len += snprintf(buf+len, sizeof(buf)-(len+1), "\n");
|
||||||
|
if (len >= (int)sizeof(buf)-1) fatal("stats too long?!");
|
||||||
|
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
write(fd, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* fill in some default stats values */
|
||||||
|
static void stats_default(unsigned counters[STATS_END])
|
||||||
|
{
|
||||||
|
counters[STATS_MAXSIZE] += DEFAULT_MAXSIZE / 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read in the stats from one dir and add to the counters */
|
||||||
|
static void stats_read_fd(int fd, unsigned counters[STATS_END])
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int len;
|
||||||
|
len = read(fd, buf, sizeof(buf)-1);
|
||||||
|
if (len <= 0) {
|
||||||
|
stats_default(counters);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf[len] = 0;
|
||||||
|
parse_stats(counters, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the stats counter for this compile */
|
||||||
|
static void stats_update_size(enum stats stat, size_t size)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
unsigned counters[STATS_END];
|
||||||
|
int need_cleanup = 0;
|
||||||
|
|
||||||
|
if (getenv("CCACHE_NOSTATS")) return;
|
||||||
|
|
||||||
|
if (!stats_file) {
|
||||||
|
if (!cache_dir) return;
|
||||||
|
x_asprintf(&stats_file, "%s/stats", cache_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open safely to try to prevent symlink races */
|
||||||
|
fd = safe_open(stats_file);
|
||||||
|
|
||||||
|
/* still can't get it? don't bother ... */
|
||||||
|
if (fd == -1) return;
|
||||||
|
|
||||||
|
memset(counters, 0, sizeof(counters));
|
||||||
|
|
||||||
|
if (lock_fd(fd) != 0) return;
|
||||||
|
|
||||||
|
/* read in the old stats */
|
||||||
|
stats_read_fd(fd, counters);
|
||||||
|
|
||||||
|
/* update them */
|
||||||
|
counters[stat]++;
|
||||||
|
|
||||||
|
/* on a cache miss we up the file count and size */
|
||||||
|
if (stat == STATS_TOCACHE) {
|
||||||
|
counters[STATS_NUMFILES] += 2;
|
||||||
|
counters[STATS_TOTALSIZE] += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* and write them out */
|
||||||
|
write_stats(fd, counters);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
/* we might need to cleanup if the cache has now got too big */
|
||||||
|
if (counters[STATS_MAXFILES] != 0 &&
|
||||||
|
counters[STATS_NUMFILES] > counters[STATS_MAXFILES]) {
|
||||||
|
need_cleanup = 1;
|
||||||
|
}
|
||||||
|
if (counters[STATS_MAXSIZE] != 0 &&
|
||||||
|
counters[STATS_TOTALSIZE] > counters[STATS_MAXSIZE]) {
|
||||||
|
need_cleanup = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_cleanup) {
|
||||||
|
char *p = dirname(stats_file);
|
||||||
|
cleanup_dir(p, counters[STATS_MAXFILES], counters[STATS_MAXSIZE]);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* record a cache miss */
|
||||||
|
void stats_tocache(size_t size)
|
||||||
|
{
|
||||||
|
/* convert size to kilobytes */
|
||||||
|
size = size / 1024;
|
||||||
|
|
||||||
|
stats_update_size(STATS_TOCACHE, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update a normal stat */
|
||||||
|
void stats_update(enum stats stat)
|
||||||
|
{
|
||||||
|
stats_update_size(stat, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read in the stats from one dir and add to the counters */
|
||||||
|
void stats_read(const char *stats_file, unsigned counters[STATS_END])
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open(stats_file, O_RDONLY|O_BINARY);
|
||||||
|
if (fd == -1) {
|
||||||
|
stats_default(counters);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lock_fd(fd);
|
||||||
|
stats_read_fd(fd, counters);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sum and display the total stats for all cache dirs */
|
||||||
|
void stats_summary(void)
|
||||||
|
{
|
||||||
|
int dir, i;
|
||||||
|
unsigned counters[STATS_END];
|
||||||
|
|
||||||
|
memset(counters, 0, sizeof(counters));
|
||||||
|
|
||||||
|
/* add up the stats in each directory */
|
||||||
|
for (dir=-1;dir<=0xF;dir++) {
|
||||||
|
char *fname;
|
||||||
|
|
||||||
|
if (dir == -1) {
|
||||||
|
x_asprintf(&fname, "%s/stats", cache_dir);
|
||||||
|
} else {
|
||||||
|
x_asprintf(&fname, "%s/%1x/stats", cache_dir, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
stats_read(fname, counters);
|
||||||
|
free(fname);
|
||||||
|
|
||||||
|
/* oh what a nasty hack ... */
|
||||||
|
if (dir == -1) {
|
||||||
|
counters[STATS_MAXSIZE] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("cache directory %s\n", cache_dir);
|
||||||
|
|
||||||
|
/* and display them */
|
||||||
|
for (i=0;stats_info[i].message;i++) {
|
||||||
|
enum stats stat = stats_info[i].stat;
|
||||||
|
|
||||||
|
if (counters[stat] == 0 &&
|
||||||
|
!(stats_info[i].flags & FLAG_ALWAYS)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s ", stats_info[i].message);
|
||||||
|
if (stats_info[i].fn) {
|
||||||
|
stats_info[i].fn(counters[stat]);
|
||||||
|
printf("\n");
|
||||||
|
} else {
|
||||||
|
printf("%8u\n", counters[stat]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* zero all the stats structures */
|
||||||
|
void stats_zero(void)
|
||||||
|
{
|
||||||
|
int dir, fd;
|
||||||
|
unsigned i;
|
||||||
|
char *fname;
|
||||||
|
unsigned counters[STATS_END];
|
||||||
|
|
||||||
|
x_asprintf(&fname, "%s/stats", cache_dir);
|
||||||
|
unlink(fname);
|
||||||
|
free(fname);
|
||||||
|
|
||||||
|
for (dir=0;dir<=0xF;dir++) {
|
||||||
|
x_asprintf(&fname, "%s/%1x/stats", cache_dir, dir);
|
||||||
|
fd = safe_open(fname);
|
||||||
|
if (fd == -1) {
|
||||||
|
free(fname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
memset(counters, 0, sizeof(counters));
|
||||||
|
lock_fd(fd);
|
||||||
|
stats_read_fd(fd, counters);
|
||||||
|
for (i=0;stats_info[i].message;i++) {
|
||||||
|
if (!(stats_info[i].flags & FLAG_NOZERO)) {
|
||||||
|
counters[stats_info[i].stat] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_stats(fd, counters);
|
||||||
|
close(fd);
|
||||||
|
free(fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* set the per directory limits */
|
||||||
|
void stats_set_limits(long maxfiles, long maxsize)
|
||||||
|
{
|
||||||
|
int dir;
|
||||||
|
unsigned counters[STATS_END];
|
||||||
|
|
||||||
|
if (maxfiles != -1) {
|
||||||
|
maxfiles /= 16;
|
||||||
|
}
|
||||||
|
if (maxsize != -1) {
|
||||||
|
maxsize /= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
create_dir(cache_dir);
|
||||||
|
|
||||||
|
/* set the limits in each directory */
|
||||||
|
for (dir=0;dir<=0xF;dir++) {
|
||||||
|
char *fname, *cdir;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
x_asprintf(&cdir, "%s/%1x", cache_dir, dir);
|
||||||
|
create_dir(cdir);
|
||||||
|
x_asprintf(&fname, "%s/stats", cdir);
|
||||||
|
free(cdir);
|
||||||
|
|
||||||
|
memset(counters, 0, sizeof(counters));
|
||||||
|
fd = safe_open(fname);
|
||||||
|
if (fd != -1) {
|
||||||
|
lock_fd(fd);
|
||||||
|
stats_read_fd(fd, counters);
|
||||||
|
if (maxfiles != -1) {
|
||||||
|
counters[STATS_MAXFILES] = maxfiles;
|
||||||
|
}
|
||||||
|
if (maxsize != -1) {
|
||||||
|
counters[STATS_MAXSIZE] = maxsize;
|
||||||
|
}
|
||||||
|
write_stats(fd, counters);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
free(fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the per directory sizes */
|
||||||
|
void stats_set_sizes(const char *dir, size_t num_files, size_t total_size)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
unsigned counters[STATS_END];
|
||||||
|
char *stats_file;
|
||||||
|
|
||||||
|
create_dir(dir);
|
||||||
|
x_asprintf(&stats_file, "%s/stats", dir);
|
||||||
|
|
||||||
|
memset(counters, 0, sizeof(counters));
|
||||||
|
|
||||||
|
fd = safe_open(stats_file);
|
||||||
|
if (fd != -1) {
|
||||||
|
lock_fd(fd);
|
||||||
|
stats_read_fd(fd, counters);
|
||||||
|
counters[STATS_NUMFILES] = num_files;
|
||||||
|
counters[STATS_TOTALSIZE] = total_size;
|
||||||
|
write_stats(fd, counters);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(stats_file);
|
||||||
|
}
|
||||||
287
CCache/test.sh
Executable file
287
CCache/test.sh
Executable file
|
|
@ -0,0 +1,287 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# a simple test suite for ccache
|
||||||
|
# tridge@samba.org
|
||||||
|
|
||||||
|
if test -n "$CC"; then
|
||||||
|
COMPILER="$CC"
|
||||||
|
else
|
||||||
|
COMPILER=cc
|
||||||
|
fi
|
||||||
|
|
||||||
|
CCACHE=../ccache
|
||||||
|
TESTDIR=test.$$
|
||||||
|
|
||||||
|
test_failed() {
|
||||||
|
reason="$1"
|
||||||
|
echo $1
|
||||||
|
$CCACHE -s
|
||||||
|
cd ..
|
||||||
|
rm -rf $TESTDIR
|
||||||
|
echo TEST FAILED
|
||||||
|
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`
|
||||||
|
done
|
||||||
|
) >> "$outfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getstat() {
|
||||||
|
stat="$1"
|
||||||
|
value=`$CCACHE -s | grep "$stat" | cut -c34-40`
|
||||||
|
echo $value
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstat() {
|
||||||
|
stat="$1"
|
||||||
|
expected_value="$2"
|
||||||
|
value=`getstat "$stat"`
|
||||||
|
if [ "$expected_value" != "$value" ]; then
|
||||||
|
test_failed "SUITE: $testsuite TEST: $testname - Expected $stat to be $expected_value got $value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
basetests() {
|
||||||
|
echo "starting testsuite $testsuite"
|
||||||
|
rm -rf .ccache
|
||||||
|
checkstat 'cache hit' 0
|
||||||
|
checkstat 'cache miss' 0
|
||||||
|
|
||||||
|
j=1
|
||||||
|
rm -f *.c
|
||||||
|
while [ $j -lt 32 ]; do
|
||||||
|
randcode test$j.c $j
|
||||||
|
j=`expr $j + 1`
|
||||||
|
done
|
||||||
|
|
||||||
|
testname="BASIC"
|
||||||
|
$CCACHE_COMPILE -c test1.c
|
||||||
|
checkstat 'cache hit' 0
|
||||||
|
checkstat 'cache miss' 1
|
||||||
|
|
||||||
|
testname="BASIC2"
|
||||||
|
$CCACHE_COMPILE -c test1.c
|
||||||
|
checkstat 'cache hit' 1
|
||||||
|
checkstat 'cache miss' 1
|
||||||
|
|
||||||
|
testname="debug"
|
||||||
|
$CCACHE_COMPILE -c test1.c -g
|
||||||
|
checkstat 'cache hit' 1
|
||||||
|
checkstat 'cache miss' 2
|
||||||
|
|
||||||
|
testname="debug2"
|
||||||
|
$CCACHE_COMPILE -c test1.c -g
|
||||||
|
checkstat 'cache hit' 2
|
||||||
|
checkstat 'cache miss' 2
|
||||||
|
|
||||||
|
testname="output"
|
||||||
|
$CCACHE_COMPILE -c test1.c -o foo.o
|
||||||
|
checkstat 'cache hit' 3
|
||||||
|
checkstat 'cache miss' 2
|
||||||
|
|
||||||
|
testname="link"
|
||||||
|
$CCACHE_COMPILE test1.c -o test 2> /dev/null
|
||||||
|
checkstat 'called for link' 1
|
||||||
|
|
||||||
|
testname="multiple"
|
||||||
|
$CCACHE_COMPILE -c test1.c test2.c
|
||||||
|
checkstat 'multiple source files' 1
|
||||||
|
|
||||||
|
testname="find"
|
||||||
|
$CCACHE blahblah -c test1.c 2> /dev/null
|
||||||
|
checkstat "couldn't find the compiler" 1
|
||||||
|
|
||||||
|
testname="bad"
|
||||||
|
$CCACHE_COMPILE -c test1.c -I 2> /dev/null
|
||||||
|
checkstat 'bad compiler arguments' 1
|
||||||
|
|
||||||
|
testname="c/c++"
|
||||||
|
ln -f test1.c test1.ccc
|
||||||
|
$CCACHE_COMPILE -c test1.ccc 2> /dev/null
|
||||||
|
checkstat 'not a C/C++ file' 1
|
||||||
|
|
||||||
|
testname="unsupported"
|
||||||
|
$CCACHE_COMPILE -M foo -c test1.c > /dev/null 2>&1
|
||||||
|
checkstat 'unsupported compiler option' 1
|
||||||
|
|
||||||
|
testname="stdout"
|
||||||
|
$CCACHE echo foo -c test1.c > /dev/null
|
||||||
|
checkstat 'compiler produced stdout' 1
|
||||||
|
|
||||||
|
testname="non-regular"
|
||||||
|
mkdir testd
|
||||||
|
$CCACHE_COMPILE -o testd -c test1.c > /dev/null 2>&1
|
||||||
|
rmdir testd
|
||||||
|
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"
|
||||||
|
CCACHE_DISABLE=1 $CCACHE_COMPILE -c test1.c 2> /dev/null
|
||||||
|
checkstat 'cache hit' 3
|
||||||
|
$CCACHE_COMPILE -c test1.c
|
||||||
|
checkstat 'cache hit' 4
|
||||||
|
|
||||||
|
testname="CCACHE_CPP2"
|
||||||
|
CCACHE_CPP2=1 $CCACHE_COMPILE -c test1.c -O -O
|
||||||
|
checkstat 'cache hit' 4
|
||||||
|
checkstat 'cache miss' 3
|
||||||
|
|
||||||
|
CCACHE_CPP2=1 $CCACHE_COMPILE -c test1.c -O -O
|
||||||
|
checkstat 'cache hit' 5
|
||||||
|
checkstat 'cache miss' 3
|
||||||
|
|
||||||
|
testname="CCACHE_NOSTATS"
|
||||||
|
CCACHE_NOSTATS=1 $CCACHE_COMPILE -c test1.c -O -O
|
||||||
|
checkstat 'cache hit' 5
|
||||||
|
checkstat 'cache miss' 3
|
||||||
|
|
||||||
|
testname="CCACHE_RECACHE"
|
||||||
|
CCACHE_RECACHE=1 $CCACHE_COMPILE -c test1.c -O -O
|
||||||
|
checkstat 'cache hit' 5
|
||||||
|
checkstat 'cache miss' 4
|
||||||
|
|
||||||
|
# strictly speaking should be 6 - RECACHE causes a double counting!
|
||||||
|
checkstat 'files in cache' 8
|
||||||
|
$CCACHE -c > /dev/null
|
||||||
|
checkstat 'files in cache' 6
|
||||||
|
|
||||||
|
|
||||||
|
testname="CCACHE_HASHDIR"
|
||||||
|
CCACHE_HASHDIR=1 $CCACHE_COMPILE -c test1.c -O -O
|
||||||
|
checkstat 'cache hit' 5
|
||||||
|
checkstat 'cache miss' 5
|
||||||
|
|
||||||
|
CCACHE_HASHDIR=1 $CCACHE_COMPILE -c test1.c -O -O
|
||||||
|
checkstat 'cache hit' 6
|
||||||
|
checkstat 'cache miss' 5
|
||||||
|
|
||||||
|
checkstat 'files in cache' 8
|
||||||
|
|
||||||
|
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' 6
|
||||||
|
checkstat 'cache miss' 6
|
||||||
|
|
||||||
|
testname="CCACHE_UNIFY"
|
||||||
|
CCACHE_UNIFY=1 $CCACHE_COMPILE -c test1.c
|
||||||
|
checkstat 'cache hit' 6
|
||||||
|
checkstat 'cache miss' 7
|
||||||
|
mv test1.c test1-saved.c
|
||||||
|
echo '/* another comment */' > test1.c
|
||||||
|
cat test1-saved.c >> test1.c
|
||||||
|
CCACHE_UNIFY=1 $CCACHE_COMPILE -c test1.c
|
||||||
|
mv test1-saved.c test1.c
|
||||||
|
checkstat 'cache hit' 7
|
||||||
|
checkstat 'cache miss' 7
|
||||||
|
|
||||||
|
testname="cache-size"
|
||||||
|
for f in *.c; do
|
||||||
|
$CCACHE_COMPILE -c $f
|
||||||
|
done
|
||||||
|
checkstat 'cache hit' 8
|
||||||
|
checkstat 'cache miss' 37
|
||||||
|
checkstat 'files in cache' 72
|
||||||
|
$CCACHE -F 48 -c > /dev/null
|
||||||
|
if [ `getstat 'files in cache'` -gt 48 ]; then
|
||||||
|
test_failed '-F test failed'
|
||||||
|
fi
|
||||||
|
|
||||||
|
testname="cpp call"
|
||||||
|
$CCACHE_COMPILE -c test1.c -E > test1.i
|
||||||
|
checkstat 'cache hit' 8
|
||||||
|
checkstat 'cache miss' 37
|
||||||
|
|
||||||
|
testname="direct .i compile"
|
||||||
|
$CCACHE_COMPILE -c test1.c
|
||||||
|
checkstat 'cache hit' 8
|
||||||
|
checkstat 'cache miss' 38
|
||||||
|
|
||||||
|
$CCACHE_COMPILE -c test1.i
|
||||||
|
checkstat 'cache hit' 9
|
||||||
|
checkstat 'cache miss' 38
|
||||||
|
|
||||||
|
$CCACHE_COMPILE -c test1.i
|
||||||
|
checkstat 'cache hit' 10
|
||||||
|
checkstat 'cache miss' 38
|
||||||
|
|
||||||
|
# removed these tests as some compilers (including newer versions of gcc)
|
||||||
|
# determine which language to use based on .ii/.i extension, and C++ may
|
||||||
|
# not be installed
|
||||||
|
# testname="direct .ii file"
|
||||||
|
# mv test1.i test1.ii
|
||||||
|
# $CCACHE_COMPILE -c test1.ii
|
||||||
|
# checkstat 'cache hit' 10
|
||||||
|
# checkstat 'cache miss' 39
|
||||||
|
|
||||||
|
# $CCACHE_COMPILE -c test1.ii
|
||||||
|
# checkstat 'cache hit' 11
|
||||||
|
# checkstat 'cache miss' 39
|
||||||
|
|
||||||
|
testname="zero-stats"
|
||||||
|
$CCACHE -z > /dev/null
|
||||||
|
checkstat 'cache hit' 0
|
||||||
|
checkstat 'cache miss' 0
|
||||||
|
|
||||||
|
testname="clear"
|
||||||
|
$CCACHE -C > /dev/null
|
||||||
|
checkstat 'files in cache' 0
|
||||||
|
|
||||||
|
|
||||||
|
rm -f test1.c
|
||||||
|
}
|
||||||
|
|
||||||
|
######
|
||||||
|
# main program
|
||||||
|
rm -rf $TESTDIR
|
||||||
|
mkdir $TESTDIR
|
||||||
|
cd $TESTDIR || exit 1
|
||||||
|
mkdir .ccache
|
||||||
|
CCACHE_DIR=.ccache
|
||||||
|
export CCACHE_DIR
|
||||||
|
|
||||||
|
testsuite="base"
|
||||||
|
CCACHE_COMPILE="$CCACHE $COMPILER"
|
||||||
|
basetests
|
||||||
|
|
||||||
|
testsuite="link"
|
||||||
|
ln -s ../ccache $COMPILER
|
||||||
|
CCACHE_COMPILE="./$COMPILER"
|
||||||
|
basetests
|
||||||
|
|
||||||
|
testsuite="hardlink"
|
||||||
|
CCACHE_COMPILE="$CCACHE $COMPILER"
|
||||||
|
CCACHE_HARDLINK=1 basetests
|
||||||
|
|
||||||
|
testsuite="cpp2"
|
||||||
|
CCACHE_COMPILE="$CCACHE $COMPILER"
|
||||||
|
CCACHE_CPP2=1 basetests
|
||||||
|
|
||||||
|
testsuite="nlevels4"
|
||||||
|
CCACHE_COMPILE="$CCACHE $COMPILER"
|
||||||
|
CCACHE_NLEVELS=4 basetests
|
||||||
|
|
||||||
|
testsuite="nlevels1"
|
||||||
|
CCACHE_COMPILE="$CCACHE $COMPILER"
|
||||||
|
CCACHE_NLEVELS=1 basetests
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
rm -rf $TESTDIR
|
||||||
|
echo test done - OK
|
||||||
|
exit 0
|
||||||
269
CCache/unify.c
Normal file
269
CCache/unify.c
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) Andrew Tridgell 2002
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, 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"
|
||||||
|
|
||||||
|
static char *s_tokens[] = {
|
||||||
|
"...", ">>=", "<<=", "+=", "-=", "*=", "/=", "%=", "&=", "^=",
|
||||||
|
"|=", ">>", "<<", "++", "--", "->", "&&", "||", "<=", ">=",
|
||||||
|
"==", "!=", ";", "{", "<%", "}", "%>", ",", ":", "=",
|
||||||
|
"(", ")", "[", "<:", "]", ":>", ".", "&", "!", "~",
|
||||||
|
"-", "+", "*", "/", "%", "<", ">", "^", "|", "?",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
#define C_ALPHA 1
|
||||||
|
#define C_SPACE 2
|
||||||
|
#define C_TOKEN 4
|
||||||
|
#define C_QUOTE 8
|
||||||
|
#define C_DIGIT 16
|
||||||
|
#define C_HEX 32
|
||||||
|
#define C_FLOAT 64
|
||||||
|
#define C_SIGN 128
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
unsigned char type;
|
||||||
|
unsigned char num_toks;
|
||||||
|
char *toks[7];
|
||||||
|
} tokens[256];
|
||||||
|
|
||||||
|
/* build up the table used by the unifier */
|
||||||
|
static void build_table(void)
|
||||||
|
{
|
||||||
|
unsigned char c;
|
||||||
|
int i;
|
||||||
|
static int done;
|
||||||
|
|
||||||
|
if (done) return;
|
||||||
|
done = 1;
|
||||||
|
|
||||||
|
memset(tokens, 0, sizeof(tokens));
|
||||||
|
for (c=0;c<128;c++) {
|
||||||
|
if (isalpha(c) || c == '_') tokens[c].type |= C_ALPHA;
|
||||||
|
if (isdigit(c)) tokens[c].type |= C_DIGIT;
|
||||||
|
if (isspace(c)) tokens[c].type |= C_SPACE;
|
||||||
|
if (isxdigit(c)) tokens[c].type |= C_HEX;
|
||||||
|
}
|
||||||
|
tokens['\''].type |= C_QUOTE;
|
||||||
|
tokens['"'].type |= C_QUOTE;
|
||||||
|
tokens['l'].type |= C_FLOAT;
|
||||||
|
tokens['L'].type |= C_FLOAT;
|
||||||
|
tokens['f'].type |= C_FLOAT;
|
||||||
|
tokens['F'].type |= C_FLOAT;
|
||||||
|
tokens['U'].type |= C_FLOAT;
|
||||||
|
tokens['u'].type |= C_FLOAT;
|
||||||
|
|
||||||
|
tokens['-'].type |= C_SIGN;
|
||||||
|
tokens['+'].type |= C_SIGN;
|
||||||
|
|
||||||
|
for (i=0;s_tokens[i];i++) {
|
||||||
|
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 */
|
||||||
|
static void pushchar(unsigned char c)
|
||||||
|
{
|
||||||
|
static unsigned char buf[64];
|
||||||
|
static int len;
|
||||||
|
|
||||||
|
if (c == 0) {
|
||||||
|
if (len > 0) {
|
||||||
|
hash_buffer((char *)buf, len);
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
hash_buffer(NULL, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[len++] = c;
|
||||||
|
if (len == 64) {
|
||||||
|
hash_buffer((char *)buf, len);
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hash some C/C++ code after unifying */
|
||||||
|
static void unify(unsigned char *p, size_t size)
|
||||||
|
{
|
||||||
|
size_t ofs;
|
||||||
|
unsigned char q;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
build_table();
|
||||||
|
|
||||||
|
for (ofs=0; ofs<size;) {
|
||||||
|
if (p[ofs] == '#') {
|
||||||
|
if ((size-ofs) > 2 && p[ofs+1] == ' ' && isdigit(p[ofs+2])) {
|
||||||
|
do {
|
||||||
|
ofs++;
|
||||||
|
} while (ofs < size && p[ofs] != '\n');
|
||||||
|
ofs++;
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
ofs++;
|
||||||
|
} while (ofs < size && p[ofs] != '\n');
|
||||||
|
pushchar('\n');
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[p[ofs]].type & C_ALPHA) {
|
||||||
|
do {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
ofs++;
|
||||||
|
} while (ofs < size &&
|
||||||
|
(tokens[p[ofs]].type & (C_ALPHA|C_DIGIT)));
|
||||||
|
pushchar('\n');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[p[ofs]].type & C_DIGIT) {
|
||||||
|
do {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
ofs++;
|
||||||
|
} while (ofs < size &&
|
||||||
|
((tokens[p[ofs]].type & C_DIGIT) || p[ofs] == '.'));
|
||||||
|
if (ofs < size && (p[ofs] == 'x' || p[ofs] == 'X')) {
|
||||||
|
do {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
ofs++;
|
||||||
|
} while (ofs < size && (tokens[p[ofs]].type & C_HEX));
|
||||||
|
}
|
||||||
|
if (ofs < size && (p[ofs] == 'E' || p[ofs] == 'e')) {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
ofs++;
|
||||||
|
while (ofs < size &&
|
||||||
|
(tokens[p[ofs]].type & (C_DIGIT|C_SIGN))) {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (ofs < size && (tokens[p[ofs]].type & C_FLOAT)) {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
pushchar('\n');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[p[ofs]].type & C_SPACE) {
|
||||||
|
do {
|
||||||
|
ofs++;
|
||||||
|
} while (ofs < size && (tokens[p[ofs]].type & C_SPACE));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[p[ofs]].type & C_QUOTE) {
|
||||||
|
q = p[ofs];
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
do {
|
||||||
|
ofs++;
|
||||||
|
while (ofs < size-1 && p[ofs] == '\\') {
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
pushchar(p[ofs+1]);
|
||||||
|
ofs+=2;
|
||||||
|
}
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
} while (ofs < size && p[ofs] != q);
|
||||||
|
pushchar('\n');
|
||||||
|
ofs++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[p[ofs]].type & C_TOKEN) {
|
||||||
|
q = p[ofs];
|
||||||
|
for (i=0;i<tokens[q].num_toks;i++) {
|
||||||
|
unsigned char *s = (unsigned char *)tokens[q].toks[i];
|
||||||
|
int len = strlen((char *)s);
|
||||||
|
if (size >= ofs+len && memcmp(&p[ofs], s, len) == 0) {
|
||||||
|
int j;
|
||||||
|
for (j=0;s[j];j++) {
|
||||||
|
pushchar(s[j]);
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
pushchar('\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i < tokens[q].num_toks) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushchar(p[ofs]);
|
||||||
|
pushchar('\n');
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
pushchar(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* hash a file that consists of preprocessor output, but remove any line
|
||||||
|
number information from the hash
|
||||||
|
*/
|
||||||
|
int unify_hash(const char *fname)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct stat st;
|
||||||
|
char *map;
|
||||||
|
|
||||||
|
fd = open(fname, O_RDONLY|O_BINARY);
|
||||||
|
if (fd == -1 || fstat(fd, &st) != 0) {
|
||||||
|
cc_log("Failed to open preprocessor output %s\n", fname);
|
||||||
|
stats_update(STATS_PREPROCESSOR);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we use mmap() to make it easy to handle arbitrarily long
|
||||||
|
lines in preprocessor output. I have seen lines of over
|
||||||
|
100k in length, so this is well worth it */
|
||||||
|
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
|
if (map == (char *)-1) {
|
||||||
|
cc_log("Failed to mmap %s\n", fname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
/* pass it through the unifier */
|
||||||
|
unify((unsigned char *)map, st.st_size);
|
||||||
|
|
||||||
|
munmap(map, st.st_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
454
CCache/util.c
Normal file
454
CCache/util.c
Normal file
|
|
@ -0,0 +1,454 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) Andrew Tridgell 2002
|
||||||
|
|
||||||
|
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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ccache.h"
|
||||||
|
|
||||||
|
static FILE *logfile;
|
||||||
|
|
||||||
|
/* log a message to the CCACHE_LOGFILE location */
|
||||||
|
void cc_log(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
extern char *cache_logfile;
|
||||||
|
|
||||||
|
if (!cache_logfile) return;
|
||||||
|
|
||||||
|
if (!logfile) logfile = fopen(cache_logfile, "a");
|
||||||
|
if (!logfile) return;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
vfprintf(logfile, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* something went badly wrong! */
|
||||||
|
void fatal(const char *msg)
|
||||||
|
{
|
||||||
|
cc_log("FATAL: %s\n", msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy all data from one file descriptor to another */
|
||||||
|
void copy_fd(int fd_in, int fd_out)
|
||||||
|
{
|
||||||
|
char buf[10240];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
while ((n = read(fd_in, buf, sizeof(buf))) > 0) {
|
||||||
|
if (write(fd_out, buf, n) != n) {
|
||||||
|
fatal("Failed to copy fd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy a file - used when hard links don't work
|
||||||
|
the copy is done via a temporary file and atomic rename
|
||||||
|
*/
|
||||||
|
int copy_file(const char *src, const char *dest)
|
||||||
|
{
|
||||||
|
int fd1, fd2;
|
||||||
|
char buf[10240];
|
||||||
|
int n;
|
||||||
|
char *tmp_name;
|
||||||
|
mode_t mask;
|
||||||
|
|
||||||
|
x_asprintf(&tmp_name, "%s.XXXXXX", dest);
|
||||||
|
|
||||||
|
fd1 = open(src, O_RDONLY|O_BINARY);
|
||||||
|
if (fd1 == -1) {
|
||||||
|
free(tmp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd2 = mkstemp(tmp_name);
|
||||||
|
if (fd2 == -1) {
|
||||||
|
close(fd1);
|
||||||
|
free(tmp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((n = read(fd1, buf, sizeof(buf))) > 0) {
|
||||||
|
if (write(fd2, buf, n) != n) {
|
||||||
|
close(fd2);
|
||||||
|
close(fd1);
|
||||||
|
unlink(tmp_name);
|
||||||
|
free(tmp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd1);
|
||||||
|
|
||||||
|
/* get perms right on the tmp file */
|
||||||
|
mask = umask(0);
|
||||||
|
fchmod(fd2, 0666 & ~mask);
|
||||||
|
umask(mask);
|
||||||
|
|
||||||
|
/* the close can fail on NFS if out of space */
|
||||||
|
if (close(fd2) == -1) {
|
||||||
|
unlink(tmp_name);
|
||||||
|
free(tmp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink(dest);
|
||||||
|
|
||||||
|
if (rename(tmp_name, dest) == -1) {
|
||||||
|
unlink(tmp_name);
|
||||||
|
free(tmp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tmp_name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* make sure a directory exists */
|
||||||
|
int create_dir(const char *dir)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if (stat(dir, &st) == 0) {
|
||||||
|
if (S_ISDIR(st.st_mode)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
errno = ENOTDIR;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (mkdir(dir, 0777) != 0 && errno != EEXIST) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
this is like asprintf() but dies if the malloc fails
|
||||||
|
note that we use vsnprintf in a rather poor way to make this more portable
|
||||||
|
*/
|
||||||
|
void x_asprintf(char **ptr, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
*ptr = NULL;
|
||||||
|
va_start(ap, format);
|
||||||
|
vasprintf(ptr, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (!ptr) fatal("out of memory in x_asprintf");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
this is like strdup() but dies if the malloc fails
|
||||||
|
*/
|
||||||
|
char *x_strdup(const char *s)
|
||||||
|
{
|
||||||
|
char *ret;
|
||||||
|
ret = strdup(s);
|
||||||
|
if (!ret) {
|
||||||
|
fatal("out of memory in strdup\n");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
this is like malloc() but dies if the malloc fails
|
||||||
|
*/
|
||||||
|
void *x_malloc(size_t size)
|
||||||
|
{
|
||||||
|
void *ret;
|
||||||
|
ret = malloc(size);
|
||||||
|
if (!ret) {
|
||||||
|
fatal("out of memory in malloc\n");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 = malloc(size);
|
||||||
|
if (!p2) {
|
||||||
|
fatal("out of memory in x_realloc");
|
||||||
|
}
|
||||||
|
if (ptr) {
|
||||||
|
memcpy(p2, ptr, size);
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
return p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
revsusive directory traversal - used for cleanup
|
||||||
|
fn() is called on all files/dirs in the tree
|
||||||
|
*/
|
||||||
|
void traverse(const char *dir, void (*fn)(const char *, struct stat *))
|
||||||
|
{
|
||||||
|
DIR *d;
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
d = opendir(dir);
|
||||||
|
if (!d) return;
|
||||||
|
|
||||||
|
while ((de = readdir(d))) {
|
||||||
|
char *fname;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (strcmp(de->d_name,".") == 0) continue;
|
||||||
|
if (strcmp(de->d_name,"..") == 0) continue;
|
||||||
|
|
||||||
|
if (strlen(de->d_name) == 0) continue;
|
||||||
|
|
||||||
|
x_asprintf(&fname, "%s/%s", dir, de->d_name);
|
||||||
|
if (lstat(fname, &st)) {
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
perror(fname);
|
||||||
|
}
|
||||||
|
free(fname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(st.st_mode)) {
|
||||||
|
traverse(fname, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(fname, &st);
|
||||||
|
free(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* return the base name of a file - caller frees */
|
||||||
|
char *str_basename(const char *s)
|
||||||
|
{
|
||||||
|
char *p = strrchr(s, '/');
|
||||||
|
if (p) {
|
||||||
|
return x_strdup(p+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return x_strdup(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return the dir name of a file - caller frees */
|
||||||
|
char *dirname(char *s)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
s = x_strdup(s);
|
||||||
|
p = strrchr(s, '/');
|
||||||
|
if (p) {
|
||||||
|
*p = 0;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lock_fd(int fd)
|
||||||
|
{
|
||||||
|
struct flock fl;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
fl.l_type = F_WRLCK;
|
||||||
|
fl.l_whence = SEEK_SET;
|
||||||
|
fl.l_start = 0;
|
||||||
|
fl.l_len = 1;
|
||||||
|
fl.l_pid = 0;
|
||||||
|
|
||||||
|
/* not sure why we would be getting a signal here,
|
||||||
|
but one user claimed it is possible */
|
||||||
|
do {
|
||||||
|
ret = fcntl(fd, F_SETLKW, &fl);
|
||||||
|
} while (ret == -1 && errno == EINTR);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return size on disk of a file */
|
||||||
|
size_t file_size(struct stat *st)
|
||||||
|
{
|
||||||
|
size_t size = st->st_blocks * 512;
|
||||||
|
if ((size_t)st->st_size > size) {
|
||||||
|
/* probably a broken stat() call ... */
|
||||||
|
size = (st->st_size + 1023) & ~1023;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* a safe open/create for read-write */
|
||||||
|
int safe_open(const char *fname)
|
||||||
|
{
|
||||||
|
int fd = open(fname, O_RDWR|O_BINARY);
|
||||||
|
if (fd == -1 && errno == ENOENT) {
|
||||||
|
fd = open(fname, O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0666);
|
||||||
|
if (fd == -1 && errno == EEXIST) {
|
||||||
|
fd = open(fname, O_RDWR|O_BINARY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display a kilobyte unsigned value in M, k or G */
|
||||||
|
void display_size(unsigned v)
|
||||||
|
{
|
||||||
|
if (v > 1024*1024) {
|
||||||
|
printf("%8.1f Gbytes", v/((double)(1024*1024)));
|
||||||
|
} else if (v > 1024) {
|
||||||
|
printf("%8.1f Mbytes", v/((double)(1024)));
|
||||||
|
} else {
|
||||||
|
printf("%8u Kbytes", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return a value in multiples of 1024 give a string that can end
|
||||||
|
in K, M or G
|
||||||
|
*/
|
||||||
|
size_t value_units(const char *s)
|
||||||
|
{
|
||||||
|
char m;
|
||||||
|
double v = atof(s);
|
||||||
|
m = s[strlen(s)-1];
|
||||||
|
switch (m) {
|
||||||
|
case 'G':
|
||||||
|
case 'g':
|
||||||
|
default:
|
||||||
|
v *= 1024*1024;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
case 'm':
|
||||||
|
v *= 1024;
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
case 'k':
|
||||||
|
v *= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (size_t)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
a sane realpath() function, trying to cope with stupid path limits and
|
||||||
|
a broken API
|
||||||
|
*/
|
||||||
|
char *x_realpath(const char *path)
|
||||||
|
{
|
||||||
|
int maxlen;
|
||||||
|
char *ret, *p;
|
||||||
|
#ifdef PATH_MAX
|
||||||
|
maxlen = PATH_MAX;
|
||||||
|
#elif defined(MAXPATHLEN)
|
||||||
|
maxlen = MAXPATHLEN;
|
||||||
|
#elif defined(_PC_PATH_MAX)
|
||||||
|
maxlen = pathconf(path, _PC_PATH_MAX);
|
||||||
|
#endif
|
||||||
|
if (maxlen < 4096) maxlen = 4096;
|
||||||
|
|
||||||
|
ret = x_malloc(maxlen);
|
||||||
|
|
||||||
|
#if HAVE_REALPATH
|
||||||
|
p = realpath(path, ret);
|
||||||
|
#else
|
||||||
|
/* 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) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ret[len] = 0;
|
||||||
|
p = ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (p) {
|
||||||
|
p = x_strdup(p);
|
||||||
|
free(ret);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a getcwd that will returns an allocated buffer */
|
||||||
|
char *gnu_getcwd(void)
|
||||||
|
{
|
||||||
|
unsigned size = 128;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
char *buffer = (char *)x_malloc(size);
|
||||||
|
if (getcwd(buffer, size) == buffer) {
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
if (errno != ERANGE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_MKSTEMP
|
||||||
|
/* cheap and nasty mkstemp replacement */
|
||||||
|
int mkstemp(char *template)
|
||||||
|
{
|
||||||
|
mktemp(template);
|
||||||
|
return open(template, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* create an empty file */
|
||||||
|
int create_empty_file(const char *fname)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
|
||||||
|
if (fd == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
return current users home directory or die
|
||||||
|
*/
|
||||||
|
const char *get_home_directory(void)
|
||||||
|
{
|
||||||
|
const char *p = getenv("HOME");
|
||||||
|
if (p) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
#ifdef HAVE_GETPWUID
|
||||||
|
{
|
||||||
|
struct passwd *pwd = getpwuid(getuid());
|
||||||
|
if (pwd) {
|
||||||
|
return pwd->pw_dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
fatal("Unable to determine home directory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
318
CCache/web/ccache-man.html
Normal file
318
CCache/web/ccache-man.html
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<html><head><title>ccache</title>
|
||||||
|
|
||||||
|
<link rev="made" href="mailto:bugs@ccache.samba.org">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h1>ccache</h1>
|
||||||
|
<h2>April 2002</h2>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h2>NAME</h2>
|
||||||
|
ccache - a fast compiler cache
|
||||||
|
<h2>SYNOPSIS</h2>
|
||||||
|
|
||||||
|
<p>ccache [OPTION]
|
||||||
|
<p>ccache <compiler> [COMPILER OPTIONS]
|
||||||
|
<p><compiler> [COMPILER OPTIONS]
|
||||||
|
<p><h2>DESCRIPTION</h2>
|
||||||
|
|
||||||
|
<p>ccache is a compiler cache. It speeds up re-compilation of C/C++ code
|
||||||
|
by caching previous compiles and detecting when the same compile is
|
||||||
|
being done again.
|
||||||
|
<p><h2>OPTIONS SUMMARY</h2>
|
||||||
|
|
||||||
|
<p>Here is a summary of the options to ccache.
|
||||||
|
<p><pre>
|
||||||
|
|
||||||
|
-s show statistics summary
|
||||||
|
-z zero statistics
|
||||||
|
-c run a cache cleanup
|
||||||
|
-C clear the cache completely
|
||||||
|
-F <maxfiles> set maximum files in cache
|
||||||
|
-M <maxsize> set maximum size of cache (use G, M or K)
|
||||||
|
-h this help page
|
||||||
|
-V print version number
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><h2>OPTIONS</h2>
|
||||||
|
|
||||||
|
<p>These options only apply when you invoke ccache as "ccache". When
|
||||||
|
invoked as a compiler none of these options apply. In that case your
|
||||||
|
normal compiler options apply and you should refer to your compilers
|
||||||
|
documentation.
|
||||||
|
<p><dl>
|
||||||
|
<p></p><dt><strong><strong>-h</strong></strong><dd> Print a options summary page
|
||||||
|
<p><p></p><dt><strong><strong>-s</strong></strong><dd> Print the current statistics summary for the cache. The
|
||||||
|
statistics are stored spread across the subdirectories of the
|
||||||
|
cache. Using "ccache -s" adds up the statistics across all
|
||||||
|
subdirectories and prints the totals.
|
||||||
|
<p><p></p><dt><strong><strong>-z</strong></strong><dd> Zero the cache statistics.
|
||||||
|
<p><p></p><dt><strong><strong>-V</strong></strong><dd> Print the ccache version number
|
||||||
|
<p><p></p><dt><strong><strong>-c</strong></strong><dd> Clean the cache and re-calculate the cache file count and
|
||||||
|
size totals. Normally the -c option should not be necessary as ccache
|
||||||
|
keeps the cache below the specified limits at runtime and keeps
|
||||||
|
statistics up to date on each compile. This option is mostly useful
|
||||||
|
if you manually modify the cache contents or believe that the cache
|
||||||
|
size statistics may be inaccurate.
|
||||||
|
<p><p></p><dt><strong><strong>-C</strong></strong><dd> Clear the entire cache, removing all cached files.
|
||||||
|
<p><p></p><dt><strong><strong>-F maxfiles</strong></strong><dd> This sets the maximum number of files allowed in
|
||||||
|
the cache. The value is stored inside the cache directory and applies
|
||||||
|
to all future compiles. Due to the way the value is stored the actual
|
||||||
|
value used is always rounded down to the nearest multiple of 16.
|
||||||
|
<p><p></p><dt><strong><strong>-M maxsize</strong></strong><dd> This sets the maximum cache size. You can specify
|
||||||
|
a value in gigabytes, megabytes or kilobytes by appending a G, M or K
|
||||||
|
to the value. The default is gigabytes. The actual value stored is
|
||||||
|
rounded down to the nearest multiple of 16 kilobytes.
|
||||||
|
<p></dl>
|
||||||
|
<p><h2>INSTALLATION</h2>
|
||||||
|
|
||||||
|
<p>There are two ways to use ccache. You can either prefix your compile
|
||||||
|
commands with "ccache" or you can create a symbolic link between
|
||||||
|
ccache and the names of your compilers. 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 compiles.
|
||||||
|
<p>To install for usage by the first method just copy ccache to somewhere
|
||||||
|
in your path.
|
||||||
|
<p>To install for the second method do something like this:
|
||||||
|
<pre>
|
||||||
|
|
||||||
|
cp ccache /usr/local/bin/
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/gcc
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/g++
|
||||||
|
ln -s /usr/local/bin/ccache /usr/local/bin/cc
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
This will work as long as /usr/local/bin comes before the path to gcc
|
||||||
|
(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.
|
||||||
|
<p>Note! Do not use a hard link, use a symbolic link. A hardlink will
|
||||||
|
cause "interesting" problems.
|
||||||
|
<p><h2>EXTRA OPTIONS</h2>
|
||||||
|
|
||||||
|
<p>When run as a compiler front end ccache usually just takes the same
|
||||||
|
command line options as the compiler you are using. The only exception
|
||||||
|
to this is the option '--ccache-skip'. That option can be used to tell
|
||||||
|
ccache that the next option is definitely not a input filename, and
|
||||||
|
should be passed along to the compiler as-is.
|
||||||
|
<p>The reason this can be important is that ccache does need to parse the
|
||||||
|
command line and determine what is an input filename and what is a
|
||||||
|
compiler option, as it needs the input filename to determine the name
|
||||||
|
of the resulting object file (among other things). The heuristic
|
||||||
|
ccache uses in this parse is that any string on the command line that
|
||||||
|
exists as a file is treated as an input file name (usually a C
|
||||||
|
file). By using --ccache-skip you can force an option to not be
|
||||||
|
treated as an input file name and instead be passed along to the
|
||||||
|
compiler as a command line option.
|
||||||
|
<p><h2>ENVIRONMENT VARIABLES</h2>
|
||||||
|
|
||||||
|
<p>ccache uses a number of environment variables to control operation. In
|
||||||
|
most cases you won't need any of these as the defaults will be fine.
|
||||||
|
<p><dl>
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_DIR</strong></strong><dd> the CCACHE_DIR environment variable specifies
|
||||||
|
where ccache will keep its cached compiler output. The default is
|
||||||
|
"$HOME/.ccache".
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_TEMPDIR</strong></strong><dd> the CCACHE_TEMPDIR environment variable specifies
|
||||||
|
where ccache will put temporary files. The default is the same as
|
||||||
|
CCACHE_DIR. Note that the CCACHE_TEMPDIR path must be on the same
|
||||||
|
filesystem as the CCACHE_DIR path, so that renames of files between
|
||||||
|
the two directories can work.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_LOGFILE</strong></strong><dd> If you set the CCACHE_LOGFILE environment
|
||||||
|
variable then ccache will write some log information on cache hits
|
||||||
|
and misses in that file. This is useful for tracking down problems.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_PATH</strong></strong><dd> You can optionally set CCACHE_PATH to a colon
|
||||||
|
separated path where ccache will look for the real compilers. If you
|
||||||
|
don't do this then ccache will look for the first executable matching
|
||||||
|
the compiler name in the normal PATH that isn't a symbolic link to
|
||||||
|
ccache itself.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_CC</strong></strong><dd> You can optionally set CCACHE_CC to force the name
|
||||||
|
of the compiler to use. If you don't do this then ccache works it out
|
||||||
|
from the command line.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_PREFIX</strong></strong><dd> This option adds a prefix to the command line
|
||||||
|
that ccache runs when invoking the compiler. Also see the section
|
||||||
|
below on using ccache with distcc.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_DISABLE</strong></strong><dd> If you set the environment variable
|
||||||
|
CCACHE_DISABLE then ccache will just call the real compiler,
|
||||||
|
bypassing the cache completely.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_READONLY</strong></strong><dd> the CCACHE_READONLY environment variable
|
||||||
|
tells ccache to attempt to use existing cached object files, but not
|
||||||
|
to try to add anything new to the cache. If you are using this because
|
||||||
|
your CCACHE_DIR is read-only, then you may find that you also need to
|
||||||
|
set CCACHE_TEMPDIR as otherwise ccache will fail to create the
|
||||||
|
temporary files.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_CPP2</strong></strong><dd> If you set the environment variable CCACHE_CPP2
|
||||||
|
then ccache will not use the optimisation of avoiding the 2nd call to
|
||||||
|
the pre-processor by compiling the pre-processed 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 the intermediate filename extensions used in
|
||||||
|
this optimisation, in which case this option could allow ccache to be
|
||||||
|
used.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_NOSTATS</strong></strong><dd> If you set the environment variable
|
||||||
|
CCACHE_NOSTATS then ccache will not update the statistics files on
|
||||||
|
each compile.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_NLEVELS</strong></strong><dd> The environment variable CCACHE_NLEVELS allows
|
||||||
|
you to choose the number of levels of hash in the cache directory. The
|
||||||
|
default is 2. The minimum is 1 and the maximum is 8.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_HARDLINK</strong></strong><dd> If you set the environment variable
|
||||||
|
CCACHE_HARDLINK then ccache will attempt to use hard links from the
|
||||||
|
cache directory when creating the compiler output rather than using a
|
||||||
|
file copy. Using hard links is faster, but can confuse programs like
|
||||||
|
'make' that rely on modification times.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_RECACHE</strong></strong><dd> This forces ccache to not use any cached
|
||||||
|
results, even if it finds them. New results are still cached, but
|
||||||
|
existing cache entries are ignored.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_UMASK</strong></strong><dd> This sets the umask for ccache and all child
|
||||||
|
processes (such as the compiler). This is mostly useful when you wish
|
||||||
|
to share your cache with other users. Note that this also affects the
|
||||||
|
file permissions set on the object files created from your
|
||||||
|
compilations.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_HASHDIR</strong></strong><dd> This tells ccache to hash the current working
|
||||||
|
directory when calculating the hash that is used to distinguish two
|
||||||
|
compiles. This prevents a problem with the storage of the current
|
||||||
|
working directory in the debug info of a 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.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_UNIFY</strong></strong><dd> If you set the environment variable CCACHE_UNIFY
|
||||||
|
then ccache will use the C/C++ unifier when hashing the pre-processor
|
||||||
|
output if -g is not used in the compile. The unifier is slower than a
|
||||||
|
normal hash, so setting this environment variable loses a little bit
|
||||||
|
of speed, but it means that ccache can take advantage of not
|
||||||
|
recompiling when the changes to the source code consist of
|
||||||
|
reformatting only. Note that using CCACHE_UNIFY changes the hash, so
|
||||||
|
cached compiles with CCACHE_UNIFY set cannot be used when
|
||||||
|
CCACHE_UNIFY is not set and vice versa. The reason the unifier is off
|
||||||
|
by default is that it can give incorrect line number information in
|
||||||
|
compiler warning messages.
|
||||||
|
<p><p></p><dt><strong><strong>CCACHE_EXTENSION</strong></strong><dd> Normally ccache tries to automatically
|
||||||
|
determine the extension to use for intermediate C pre-processor files
|
||||||
|
based on the type of file being compiled. Unfortunately this sometimes
|
||||||
|
doesn't work, for example when using the aCC compiler on HP-UX. On
|
||||||
|
systems like this you can use the CCACHE_EXTENSION option to override
|
||||||
|
the default. On HP-UX set this environment variable to "i" if you use
|
||||||
|
the aCC compiler.
|
||||||
|
<p></dl>
|
||||||
|
<p><h2>CACHE SIZE MANAGEMENT</h2>
|
||||||
|
|
||||||
|
<p>By default ccache has a one gigabyte limit on the cache size and no
|
||||||
|
maximum number of files. You can set a different limit using the
|
||||||
|
"ccache -M" and "ccache -F" options, which set the size and number of
|
||||||
|
files limits.
|
||||||
|
<p>When these limits are reached ccache will reduce the cache to 20%
|
||||||
|
below the numbers you specified in order to avoid doing the cache
|
||||||
|
clean operation too often.
|
||||||
|
<p><h2>HOW IT WORKS</h2>
|
||||||
|
|
||||||
|
<p>The basic idea is to detect when you are compiling exactly the same
|
||||||
|
code a 2nd time and use the previously compiled output. You detect
|
||||||
|
that it is the same code by forming a hash of:
|
||||||
|
<p><ul>
|
||||||
|
<li > the pre-processor output from running the compiler with -E
|
||||||
|
<li > the command line options
|
||||||
|
<li > the real compilers size and modification time
|
||||||
|
<li > any stderr output generated by the compiler
|
||||||
|
</ul>
|
||||||
|
<p>These are hashed using md4 (a strong hash) and a cache file is formed
|
||||||
|
based on that hash result. When the same compilation is done a second
|
||||||
|
time ccache is able to supply the correct compiler output (including
|
||||||
|
all warnings etc) from the cache.
|
||||||
|
<p>ccache has been carefully written to always produce exactly the same
|
||||||
|
compiler output that you would get without the cache. If you ever
|
||||||
|
discover a case where ccache changes the output of your compiler then
|
||||||
|
please let me know.
|
||||||
|
<p><h2>USING CCACHE WITH DISTCC</h2>
|
||||||
|
|
||||||
|
<p>distcc is a very useful program for distributing compilation across a
|
||||||
|
range of compiler servers. It is often useful to combine distcc with
|
||||||
|
ccache, so that compiles that are done are sped up by distcc, but that
|
||||||
|
ccache avoids the compile completely where possible.
|
||||||
|
<p>To use distcc with ccache I recommend using the CCACHE_PREFIX
|
||||||
|
option. You just need to set the environment variable CCACHE_PREFIX to
|
||||||
|
'distcc' and ccache will prefix the command line used with the
|
||||||
|
compiler with the command 'distcc'.
|
||||||
|
<p><h2>SHARING A CACHE</h2>
|
||||||
|
|
||||||
|
<p>A group of developers can increase the cache hit rate by sharing a
|
||||||
|
cache directory. The hard links however cause unwanted side effects,
|
||||||
|
as all links to a cached file share the file's modification timestamp.
|
||||||
|
This results in false dependencies to be triggered by timestamp-based
|
||||||
|
build systems whenever another user links to an existing
|
||||||
|
file. Typically, users will see that their libraries and binaries are
|
||||||
|
relinked without reason. To share a cache without side effects, the
|
||||||
|
following conditions need to be met:
|
||||||
|
<p><ul>
|
||||||
|
<li > Use the same <strong>CCACHE_DIR</strong> environment variable setting
|
||||||
|
<li > Set the <strong>CCACHE_NOLINK</strong> environment variable
|
||||||
|
<li > Make sure everyone sets the CCACHE_UMASK environment variable
|
||||||
|
to 002, this ensures that cached files are accessible to everyone in
|
||||||
|
the group.
|
||||||
|
<li > Make sure that all users have write permission in the entire
|
||||||
|
cache directory (and that you trust all users of the shared cache).
|
||||||
|
<li > Make sure that the setgid bit is set on all directories in the
|
||||||
|
cache. This tells the filesystem to inherit group ownership for new
|
||||||
|
directories. The command "chmod g+s `find $CCACHE_DIR -type d`" might
|
||||||
|
be useful for this.
|
||||||
|
</ul>
|
||||||
|
<p><h2>HISTORY</h2>
|
||||||
|
|
||||||
|
<p>ccache was inspired by the compilercache shell script script written
|
||||||
|
by Erik Thiele and I would like to thank him for an excellent piece of
|
||||||
|
work. See
|
||||||
|
<a href="http://www.erikyyy.de/compilercache/">http://www.erikyyy.de/compilercache/</a>
|
||||||
|
for the Erik's scripts.
|
||||||
|
<p>I wrote ccache because I wanted to get a bit more speed out of a
|
||||||
|
compiler cache and I wanted to remove some of the limitations of the
|
||||||
|
shell-script version.
|
||||||
|
<p><h2>DIFFERENCES FROM COMPILERCACHE</h2>
|
||||||
|
|
||||||
|
<p>The biggest differences between Erik's compilercache script and ccache
|
||||||
|
are:
|
||||||
|
<ul>
|
||||||
|
<li > ccache is written in C, which makes it a bit faster (calling out to
|
||||||
|
external programs is mostly what slowed down the scripts).
|
||||||
|
<li > ccache can automatically find the real compiler
|
||||||
|
<li > ccache keeps statistics on hits/misses
|
||||||
|
<li > ccache can do automatic cache management
|
||||||
|
<li > ccache can cache compiler output that includes warnings. In many
|
||||||
|
cases this gives ccache a much higher cache hit rate.
|
||||||
|
<li > ccache can handle a much wider ranger of compiler options
|
||||||
|
<li > ccache avoids a double call to cpp on a cache miss
|
||||||
|
</ul>
|
||||||
|
<p><h2>BUGS</h2>
|
||||||
|
|
||||||
|
<p>When the cache is stored on an NFS filesystem, the filesystem must be
|
||||||
|
exported with the <strong>no_subtree_check</strong> option to make renames between
|
||||||
|
directories reliable.
|
||||||
|
<p><h2>CREDITS</h2>
|
||||||
|
|
||||||
|
<p>Thanks to the following people for their contributions to ccache
|
||||||
|
<ul>
|
||||||
|
<li > Erik Thiele for the original compilercache script
|
||||||
|
<li > Luciano Rocha for the idea of compiling the pre-processor output
|
||||||
|
to avoid a 2nd cpp pass
|
||||||
|
<li > Paul Russell for many suggestions and the debian packaging
|
||||||
|
</ul>
|
||||||
|
<p><h2>AUTHOR</h2>
|
||||||
|
|
||||||
|
<p>ccache was written by Andrew Tridgell
|
||||||
|
<a href="http://samba.org/~tridge/">http://samba.org/~tridge/</a>
|
||||||
|
<p>If you wish to report a problem or make a suggestion then please email
|
||||||
|
bugs@ccache.samba.org
|
||||||
|
<p>ccache is released under the GNU General Public License version 2 or
|
||||||
|
later. Please see the file COPYING for license details.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
158
CCache/web/index.html
Normal file
158
CCache/web/index.html
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>ccache</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY BGCOLOR="#ffffff" TEXT="#000000" VLINK="#292555" LINK="#292555" ALINK="#cc0033">
|
||||||
|
<h2>ccache</h2>
|
||||||
|
|
||||||
|
ccache is a compiler cache. It acts as a caching pre-processor to
|
||||||
|
C/C++ compilers, using the -E compiler switch and a hash to detect
|
||||||
|
when a compilation can be satisfied from cache. This often results in
|
||||||
|
a 5 to 10 times speedup in common compilations.<p>
|
||||||
|
|
||||||
|
The idea came from Erik Thiele wrote the original <a
|
||||||
|
href="http://compilercache.sourceforge.net/">compilercache</a> program
|
||||||
|
as a bourne shell script. ccache is a re-implementation of Erik's idea
|
||||||
|
in C with more features and better performance.<p>
|
||||||
|
|
||||||
|
<h2>Latest release</h2>
|
||||||
|
|
||||||
|
The latest release is ccache 2.4.
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Added CCACHE_READONLY option
|
||||||
|
<li>Added CCACHE_TEMPDIR option
|
||||||
|
<li>fixed handling of hard-linked compilers on AIX
|
||||||
|
<li>added O_BINARY support, to try and support win32 compiles
|
||||||
|
<li>show cache directory in stats output
|
||||||
|
<li>fixed handling of HOME environment variable
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
See the <a href="/ccache/ccache-man.html">manual page</a> for details
|
||||||
|
on the new options.<p>
|
||||||
|
|
||||||
|
You can get this release from the <a href="/ftp/ccache/">download directory</a>
|
||||||
|
|
||||||
|
<p>NOTE! This release changes the hash input slighly, so you will
|
||||||
|
probably find that you will not get any hits against your existing
|
||||||
|
cache when you upgrade.
|
||||||
|
|
||||||
|
<h2>Why bother?</h2>
|
||||||
|
|
||||||
|
Why bother with a compiler cache? If you ever run "make clean; make"
|
||||||
|
then you can probably benefit from ccache. It is very common for
|
||||||
|
developers to do a clean build of a project for a whole host of
|
||||||
|
reasons, and this throws away all the information from your previous
|
||||||
|
compiles.<p>
|
||||||
|
|
||||||
|
By using ccache you can get exactly the same effect as "make clean;
|
||||||
|
make" but much faster. It also helps a lot when doing RPM builds,
|
||||||
|
as RPM can make doing incremental builds tricky.<p>
|
||||||
|
|
||||||
|
I put the effort into writing ccache for 2 reasons. The first is the
|
||||||
|
Samba build farm
|
||||||
|
(<a href="http://build.samba.org/">http://build.samba.org/</a>)
|
||||||
|
which constantly does clean builds of Samba on about 30 machines after each
|
||||||
|
CVS commit. On some of those machines the build took over an hour. By
|
||||||
|
using ccache we get the same effect as clean builds but about 6 times
|
||||||
|
faster.<p>
|
||||||
|
|
||||||
|
The second reason is the autobuild system I used to run for
|
||||||
|
Quantum. That system builds our whole Linux based OS from scratch
|
||||||
|
after every CVS commit to catch compilation problems quickly. Using
|
||||||
|
ccache those builds are much faster.
|
||||||
|
|
||||||
|
<h2>Is it safe?</h2>
|
||||||
|
|
||||||
|
Yes. The most important aspect of a compiler cache is to <b>always</b>
|
||||||
|
produce exactly the same output that the real compiler would
|
||||||
|
produce. The includes providing exactly the same object files and
|
||||||
|
exactly the same compiler warnings that would be produced if you use
|
||||||
|
the real compiler. The only way you should be able to tell that you
|
||||||
|
are using ccache is the speed.<p>
|
||||||
|
|
||||||
|
I have coded ccache very carefully to try to provide these guarantees.
|
||||||
|
|
||||||
|
<h2>Features</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> keeps statistics on hits/misses
|
||||||
|
<li> automatic cache size management
|
||||||
|
<li> can cache compiles that generate warnings
|
||||||
|
<li> easy installation
|
||||||
|
<li> very low overhead
|
||||||
|
<li> uses hard links where possible to avoid copies
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Documentation</h2>
|
||||||
|
|
||||||
|
See the <a href="/ccache/ccache-man.html">manual page</a>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Performance</h2>
|
||||||
|
|
||||||
|
Here are some results for compiling Samba on my Linux laptop. I have
|
||||||
|
also included the results of using Erik's compilercache program
|
||||||
|
(version 1.0.10) for comparison.<p>
|
||||||
|
|
||||||
|
<table border=1>
|
||||||
|
<tr><th> </th> <th> ccache</th> <th> compilercache</th> </tr>
|
||||||
|
<tr><td>normal </td> <td align=right>13m 4s </td><td align=right>13m 4s</td> </tr>
|
||||||
|
<tr><td>uncached </td> <td align=right>13m 15s </td><td align=right>15m 41s</td> </tr>
|
||||||
|
<tr><td>cached </td> <td align=right>2m 45s </td><td align=right>4m 26s</td> </tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>How to use it</h2>
|
||||||
|
|
||||||
|
You can use ccache in two ways. The first is just to prefix your
|
||||||
|
compile commands with "ccache". For example, you could change the
|
||||||
|
"CC=gcc" line in your Makefile to be "CC=ccache gcc".<p>
|
||||||
|
|
||||||
|
Alternatively, you can create symbolic links from your compilers name
|
||||||
|
to ccache. This allows you to use ccache without any changes to your
|
||||||
|
build system.
|
||||||
|
|
||||||
|
<h2>Download</h2>
|
||||||
|
|
||||||
|
You can download the latest release from the <a
|
||||||
|
href="/ftp/ccache/">download directory</a>.<p>
|
||||||
|
|
||||||
|
For the bleeding edge, you can fetch ccache via CVS or
|
||||||
|
rsync. To fetch via cvs use the following command:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
cvs -d :pserver:cvs@pserver.samba.org:/cvsroot co ccache
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
To fetch via rsync use this command:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
rsync -Pavz samba.org::ftp/unpacked/ccache .
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h2>Related projects</h2>
|
||||||
|
|
||||||
|
Here are some related programs you may find interesting
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> <a href="http://distcc.samba.org/">distcc</a> - a distributed compilation system
|
||||||
|
<li> <a href="http://cachecc1.sourceforge.net/">cachecc1</a> - a gcc specific cache
|
||||||
|
<li> <a href="http://sourceforge.net/projects/gocache/">gocache</a> - a cross platform compiler cache
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<h2>Mailing list</h2>
|
||||||
|
|
||||||
|
<p>A <a href="http://lists.samba.org/mailman/listinfo/ccache/">mailing
|
||||||
|
list</a> is available for discussion of ccache.
|
||||||
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<tiny>
|
||||||
|
<a href="http://samba.org/~tridge/">Andrew Tridgell</a><br>
|
||||||
|
<a href="mailto:bugs@ccache.samba.org">bugs@ccache.samba.org</a>
|
||||||
|
</tiny>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue