First kinda/sorta working version of the Archive library. Reading is not
[oota-llvm.git] / lib / Archive / ArchiveWriter.cpp
1 //===-- ArchiveWriter.cpp - LLVM archive writing --------------------------===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by Reid Spencerand is distributed under the 
6 // University of Illinois Open Source License. See LICENSE.TXT for details.
7 // 
8 //===----------------------------------------------------------------------===//
9 //
10 // Builds up standard unix archive files (.a) containing LLVM bytecode.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "ArchiveInternals.h"
15 #include "llvm/Module.h"
16 #include "llvm/Bytecode/Reader.h"
17 #include "llvm/Support/FileUtilities.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/System/MappedFile.h"
20 #include <fstream>
21 #include <iostream>
22
23 using namespace llvm;
24
25 Archive* 
26 Archive::CreateEmpty(const sys::Path& Filename) {
27   Archive* result = new Archive;
28   Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals;
29   impl->fname = Filename;
30   return result;
31 }
32
33 Archive*
34 Archive::CreateFromFiles(
35   const sys::Path& Filename,
36   const PathList& Files,
37   const std::string& StripName
38 ) {
39   Archive* result = new Archive;
40   Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals;
41   impl->fname = Filename;
42
43   try {
44     size_t strip_len = StripName.length();
45     for (PathList::const_iterator P = Files.begin(), E = Files.end(); P != E ;++P)
46     {
47       if (P->readable()) {
48         std::string name(P->get());
49         if (strip_len > 0 && StripName == name.substr(0,strip_len)) {
50           name.erase(0,strip_len);
51         }
52         if (P->isBytecodeFile()) {
53           std::vector<std::string> syms;
54           if (!GetBytecodeSymbols(*P, syms))
55             throw std::string("Can not get symbols from: ") + P->get();
56           impl->addFileMember(*P, name, &syms);
57         } else {
58           impl->addFileMember(*P, name);
59         }
60       }
61       else
62         throw std::string("Can not read: ") + P->get();
63     }
64
65     // Now that we've collected everything, write the archive
66     impl->writeArchive();
67
68   } catch(...) {
69     delete impl;
70     result->impl = 0;
71     delete result;
72     throw;
73   }
74
75   return result;
76 }
77
78 void
79 Archive::ArchiveInternals::addFileMember(
80   const sys::Path& filePath,
81   const std::string& memberName,
82   const StrTab* symbols
83 ) {
84   MemberInfo info;
85   info.path = filePath;
86   info.name = memberName;
87   filePath.getStatusInfo(info.status);
88   if (symbols)
89     info.symbols = *symbols;
90   info.offset = 0;
91   members.push_back(info);
92 }
93
94 void
95 Archive::ArchiveInternals::writeInteger(int num, std::ofstream& ARFile) {
96   char buff[4];
97   buff[0] = (num >> 24) & 255;
98   buff[1] = (num >> 16) & 255;
99   buff[2] = (num >> 8) & 255;
100   buff[3] = num & 255;
101   ARFile.write(buff, sizeof(buff));
102 }
103
104 void
105 Archive::ArchiveInternals::writeSymbolTable( std::ofstream& ARFile ) {
106  
107   // Compute the number of symbols in the symbol table and the
108   // total byte size of the string pool. While we're traversing,
109   // build the string pool for supporting long file names. Also,
110   // build the table of file offsets for the symbol table and 
111   // the 
112   typedef std::map<std::string,unsigned> SymbolMap;
113   StrTab stringPool;
114   SymbolMap symbolTable;
115   std::vector<unsigned> fileOffsets;
116   std::string symTabStrings;
117   unsigned fileOffset = 0;
118   unsigned spOffset = 0;
119   unsigned numSymbols = 0;
120   unsigned numSymBytes = 0;
121   for (unsigned i = 0; i < members.size(); i++ ) {
122     MemberInfo& mi = members[i];
123     StrTab& syms = mi.symbols;
124     size_t numSym = syms.size();
125     numSymbols += numSym;
126     for (unsigned j = 0; j < numSym; j++ ) {
127       numSymBytes += syms[j].size() + 1;
128       symbolTable[syms[i]] = i;
129     }
130     if (mi.name.length() > 15 || std::string::npos != mi.name.find('/')) {
131       stringPool.push_back(mi.name + "/\n");
132       mi.name = std::string("/") + utostr(spOffset);
133       spOffset += mi.name.length() + 2;
134     } else if (mi.name[mi.name.length()-1] != '/') {
135       mi.name += "/";
136     }
137     fileOffsets.push_back(fileOffset);
138     fileOffset += sizeof(ArchiveMemberHeader) + mi.status.fileSize;
139   }
140
141
142   // Compute the size of the symbol table file member
143   unsigned symTabSize = 0;
144   if (numSymbols != 0) 
145     symTabSize = 
146       sizeof(ArchiveMemberHeader) + // Size of the file header
147       4 +                           // Size of "number of entries"
148       (4 * numSymbols) +            // Size of member file indices
149       numSymBytes;                  // Size of the string table
150
151   // Compute the size of the string pool
152   unsigned strPoolSize = 0;
153   if (spOffset != 0 )
154     strPoolSize = 
155       sizeof(ArchiveMemberHeader) + // Size of the file header
156       spOffset;                     // Number of bytes in the string pool
157
158   // Compute the byte index offset created by symbol table and string pool
159   unsigned firstFileOffset = symTabSize + strPoolSize;
160
161   // Create header for symbol table. This must be first if there is
162   // a symbol table and must have a special name.
163   if ( symTabSize > 0 ) {
164     ArchiveMemberHeader Hdr;
165     Hdr.init();
166
167     // Name of symbol table is '/               ' but "" is passed in
168     // because the setName method always terminates with a /
169     Hdr.setName(ARFILE_SYMTAB_NAME);
170     Hdr.setDate();
171     Hdr.setSize(symTabSize - sizeof(ArchiveMemberHeader));
172     Hdr.setMode(0);
173     Hdr.setUid(0);
174     Hdr.setGid(0);
175     
176     // Write header to archive file
177     ARFile.write((char*)&Hdr, sizeof(Hdr));
178     
179     // Write the number of entries in the symbol table
180     this->writeInteger(numSymbols, ARFile);
181
182     // Write the file offset indices for each symbol and build the
183     // symbol table string pool
184     std::string symTabStrPool;
185     symTabStrPool.reserve(256 * 1024); // Reserve 256KBytes for symbols
186     for (SymbolMap::iterator I = symbolTable.begin(), E = symbolTable.end();
187          I != E; ++I ) {
188       this->writeInteger(firstFileOffset + fileOffsets[I->second], ARFile);
189       symTabStrPool += I->first;
190       symTabStrPool += "\0";
191     }
192
193     // Write the symbol table's string pool
194     ARFile.write(symTabStrPool.data(), symTabStrPool.size());
195   }
196
197   //============== DONE WITH SYMBOL TABLE 
198
199   if (strPoolSize > 0) {
200     // Initialize the header for the string pool
201     ArchiveMemberHeader Hdr;
202     Hdr.init();
203     Hdr.setName(ARFILE_STRTAB_NAME); 
204     Hdr.setDate();
205     Hdr.setSize(spOffset);
206     Hdr.setMode(0);
207     Hdr.setUid(0);
208     Hdr.setGid(0);
209
210     // Write the string pool header
211     ARFile.write((char*)&Hdr, sizeof(Hdr));
212
213     // Write the string pool
214     for (unsigned i = 0; i < stringPool.size(); i++) {
215       ARFile.write(stringPool[i].data(), stringPool[i].size());
216     }
217   }
218 }
219
220 void
221 Archive::ArchiveInternals::writeMember(
222   const MemberInfo& member,
223   std::ofstream& ARFile
224 ) {
225
226   // Map the file into memory. We do this early for two reasons. First,
227   // if there's any kind of error, we want to know about it. Second, we
228   // want to ensure we're using the most recent size for this file.
229   sys::MappedFile mFile(member.path);
230   mFile.map();
231
232   // Header for the archive member
233   ArchiveMemberHeader Hdr;
234   Hdr.init();
235
236   // Set the name. If its longer than 15 chars, it will have already
237   // been reduced by the writeSymbolTable.
238   Hdr.setName(member.name);
239
240   // Set the other header members
241   Hdr.setSize( mFile.size() );
242   Hdr.setMode( member.status.mode);
243   Hdr.setUid ( member.status.user);
244   Hdr.setGid ( member.status.group);
245   Hdr.setDate( member.status.modTime.ToPosixTime() );
246
247   // Write header to archive file
248   ARFile.write((char*)&Hdr, sizeof(Hdr));
249   
250   //write to archive file
251   ARFile.write(mFile.charBase(),mFile.size());
252
253   mFile.unmap();
254 }
255
256 void
257 Archive::ArchiveInternals::writeArchive() {
258   
259   // Create archive file for output.
260   std::ofstream ArchiveFile(fname.get().c_str());
261   
262   // Check for errors opening or creating archive file.
263   if ( !ArchiveFile.is_open() || ArchiveFile.bad() ) {
264     throw std::string("Error opening archive file: ") + fname.get();
265   }
266
267   // Write magic string to archive.
268   ArchiveFile << ARFILE_MAGIC;
269
270   // Write the symbol table and string pool
271   writeSymbolTable(ArchiveFile);
272
273   //Loop over all member files, and add to the archive.
274   for ( unsigned i = 0; i < members.size(); ++i) {
275     if(ArchiveFile.tellp() % 2 != 0)
276       ArchiveFile << ARFILE_PAD;
277     writeMember(members[i],ArchiveFile);
278   }
279
280   //Close archive file.
281   ArchiveFile.close();
282 }
283
284 // vim: sw=2 ai