// @(#)root/winnt:$Id$ // Author: Axel Naumann 2006-05-09 /************************************************************************* * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ // WiX MSI Installer Package Utility // Creates a WiX source file // USAGE: makemsi outputfile.msi -T filelist.txt // will create a MSI file for files in filelist.txt #if !defined(VERSION) || !defined(PRODUCT) # error "Define the CPP macros PRODUCT and VERSION!" #endif #include #include #include #include #include #include #include #include #include using std::string; using std::list; using std::map; using std::cerr; using std::endl; using std::ostream; //////////////////////////////////////////////////////////////////////////////// // CLASS DECLARATIONS //////////////////////////////////////////////////////////////////////////////// class MSIDir; class MSIDirEntry { public: MSIDirEntry(const char* name, MSIDir* parent, bool dir); virtual ~MSIDirEntry() {} string GetLongName() const {return fLongName;} string GetShortName() const {return fShortName;} string GetPath() const {return fPath;} string GetId() const; MSIDir* GetParent() const {return fParent;} virtual void WriteRecurse(std::ostream& out, string indent) const = 0; std::ostream& WriteLongShort(std::ostream& out) const; private: void SetShortName(bool dir); string GetMyId() const; string fLongName; // regular name string fShortName; // 8.3 name string fPath; // path incl parents MSIDir* fParent; // parent dir }; //////////////////////////////////////////////////////////////////////////////// class MSIFile; class MSIDir: public MSIDirEntry { public: MSIDir(const char* name, MSIDir* parent=0): MSIDirEntry(name, parent, true) {} ~MSIDir() {if (!GetParent()) UpdateGuids();} void AddFile(const char* file); void Write(std::ostream& out) const; private: void WriteRecurse(std::ostream& out, string indent) const; void WriteComponentsRecurse(std::ostream& out, string indent) const; const char* GetGuid() const { if (!fgGuids.size() && !fgNewGuids.size()) SetupGuids(); std::map::const_iterator iGuid = fgGuids.find(GetId()); if (iGuid == fgGuids.end()) return CreateGuid(); return iGuid->second.c_str(); } const char* CreateGuid() const; static void SetupGuids(); static void UpdateGuids(); std::map fSubdirs; std::list fFiles; static std::map fgGuids; static std::map fgNewGuids; // guids created during this process static const char* fgGuidFileName; // location of the GUID file }; //////////////////////////////////////////////////////////////////////////////// class MSIFile: public MSIDirEntry { public: MSIFile(const char* name, MSIDir* parent): MSIDirEntry(name, parent, false) {} void WriteRecurse(std::ostream& out, string indent) const { out << indent << "" << std::endl; }; }; //////////////////////////////////////////////////////////////////////////////// // MSIDirEntry DEFINITIONS //////////////////////////////////////////////////////////////////////////////// MSIDirEntry::MSIDirEntry(const char* name, MSIDir* parent, bool dir): fLongName(name), fParent(parent) { if (fParent) fPath = fParent->GetPath() + '/' + name; else fPath = "."; SetShortName(dir); } void MSIDirEntry::SetShortName(bool dir) { WIN32_FIND_DATA findData; string filename(GetPath()); HANDLE hFind = ::FindFirstFile(filename.c_str(), &findData); if (hFind == INVALID_HANDLE_VALUE) { std::cerr << "Cannot find " << filename << std::endl; } else { bool foundDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0; if (foundDir == !dir) std::cerr << filename << " is not what I expected it to be!" << std::endl; else fShortName = findData.cAlternateFileName; } FindClose(hFind); } string MSIDirEntry::GetId() const { string ret; if (fParent) ret = fParent->GetId() + "_"; return ret + GetMyId(); } string MSIDirEntry::GetMyId() const { string ret(fLongName); std::replace(ret.begin(), ret.end(), '/', '_'); std::replace(ret.begin(), ret.end(), '-', '_'); std::replace(ret.begin(), ret.end(), '#', '_'); std::replace(ret.begin(), ret.end(), '~', '_'); std::replace(ret.begin(), ret.end(), '@', '_'); return ret; } std::ostream& MSIDirEntry::WriteLongShort(std::ostream& out) const { if (!fShortName.empty()) out << "LongName=\"" << fLongName <<"\" Name=\"" << fShortName << "\" "; else out << "Name=\"" << fLongName << "\" "; return out; } //////////////////////////////////////////////////////////////////////////////// // MSIDir DEFINITIONS //////////////////////////////////////////////////////////////////////////////// std::map MSIDir::fgGuids; std::map MSIDir::fgNewGuids; const char* MSIDir::fgGuidFileName = 0; // set to e.g. "guids.txt" make GUIDs persistent void MSIDir::AddFile(const char* file) { string subdir(file); string filename(file); string::size_type posSlash = subdir.find('/'); if (posSlash != string::npos) { subdir.erase(posSlash, subdir.length()); filename.erase(0, posSlash+1); } else subdir.erase(); if (filename.empty()) { std::cerr << "Cannot add empty filename!" << std::endl; return; } if (subdir.empty()) fFiles.push_back(new MSIFile(filename.c_str(), this)); else { if (!fSubdirs[subdir]) fSubdirs[subdir] = new MSIDir(subdir.c_str(), this); fSubdirs[subdir]->AddFile(filename.c_str()); } } void MSIDir::Write(std::ostream& out) const { const DWORD bufsize = MAX_PATH; char pwd[bufsize]; DWORD len = ::GetCurrentDirectory(bufsize, pwd); if (len > 0 && pwd[len - 1] == '\\') { pwd[len - 1] = 0; } out << "" << std::endl; out << "" << std::endl; out << "" << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; WriteRecurse(out, std::string(" ")); out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; WriteComponentsRecurse(out, std::string(" ")); out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << " " << std::endl; out << "" << std::endl; out << "" << std::endl; } void MSIDir::WriteRecurse(std::ostream& out, string indent) const { // write to out recursively if (!fFiles.size() && !fSubdirs.size()) return; if (GetParent()) { // assume that Write takes care of the root dir. out << indent << "" << std::endl; indent+=" "; } if (fFiles.size()) { out << indent << "" << std::endl; indent+=" "; for (std::list::const_iterator iFile = fFiles.begin(); iFile != fFiles.end(); ++iFile) { (*iFile)->WriteRecurse(out, indent); } indent.erase(indent.length()-3, 3); out << indent << "" << std::endl; } for (std::map::const_iterator iSubdir = fSubdirs.begin(); iSubdir != fSubdirs.end(); ++iSubdir) iSubdir->second->WriteRecurse(out, indent); indent.erase(indent.length()-3, 3); if (GetParent()) { // assume that Write takes care of the root dir. out << indent << "" << std::endl; } } void MSIDir::WriteComponentsRecurse(std::ostream& out, string indent) const { // write all components to out if (!fFiles.empty()) out << indent << "" << std::endl; for (std::map::const_iterator iSubdir = fSubdirs.begin(); iSubdir != fSubdirs.end(); ++iSubdir) iSubdir->second->WriteComponentsRecurse(out, indent); } const char* MSIDir::CreateGuid() const { UUID uuid; ::UuidCreate(&uuid); unsigned char* str = 0; ::UuidToString(&uuid, &str); std::string id = GetId(); const std::string& ret = fgGuids[id] = (char*)str; fgNewGuids[id] = ret; RpcStringFree(&str); return ret.c_str(); } void MSIDir::SetupGuids() { if (!fgGuidFileName) return; std::ifstream in(fgGuidFileName); std::string line; while (std::getline(in, line)) { std::istringstream sin(line); std::string id, guid; sin >> id >> guid; fgGuids[id] = guid; } } void MSIDir::UpdateGuids() { if (!fgNewGuids.size() || !fgGuidFileName) return; std::ofstream out(fgGuidFileName, std::ios_base::app); if (!out) { std::cerr << "ERROR: cannot write to GUID file " << fgGuidFileName << "!" << std::endl; std::cerr << "ERROR: You should NOT use this MSI file, but re-generate with with accessible GUID file!" << std::endl; return; } for (std::map::const_iterator iGuid = fgNewGuids.begin(); iGuid != fgNewGuids.end(); ++iGuid) out << iGuid->first << " " << iGuid->second << std::endl; std::cout << "WARNING: new GUIDs created; please cvs checkin " << fgGuidFileName << "!" << std::endl; } //////////////////////////////////////////////////////////////////////////////// // main() //////////////////////////////////////////////////////////////////////////////// int main(int argc, char *argv[]) { if (argc<4 || string(argv[2]) != "-T") { std::cerr << "USAGE: " << argv[0] << " -T " << std::endl; return 1; } string outfile = argv[1]; std::ofstream out(outfile.c_str()); if (!out) { std::cerr << "Cannot open output file " << outfile << "!" << std::endl; return 2; } string infile = argv[3]; std::ifstream in(infile.c_str()); if (!in) { std::cerr << "Cannot open input file " << infile << "!" << std::endl; return 2; } MSIDir fileroot("ROOTSYS"); string line; while (std::getline(in, line)) fileroot.AddFile(line.c_str()); fileroot.Write(out); }