/* * * Copyright 2004 The Ant-Contrib project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sf.antcontrib.cpptasks.platforms; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Writer; import net.sf.antcontrib.cpptasks.CUtil; import net.sf.antcontrib.cpptasks.TargetMatcher; import net.sf.antcontrib.cpptasks.VersionInfo; import net.sf.antcontrib.cpptasks.compiler.LinkType; /** * Platform specific behavior for Microsoft Windows. * * @author Curt Arnold */ public final class WindowsPlatform { /** * Constructor. */ private WindowsPlatform() { } /** * Adds source or object files to the bidded fileset to * support version information. * * @param versionInfo version information * @param linkType link type * @param isDebug true if debug build * @param outputFile name of generated executable * @param objDir directory for generated files * @param matcher bidded fileset * @throws IOException if unable to write version resource */ public static void addVersionFiles(final VersionInfo versionInfo, final LinkType linkType, final File outputFile, final boolean isDebug, final File objDir, final TargetMatcher matcher) throws IOException { if (versionInfo == null) { throw new NullPointerException("versionInfo"); } if (linkType == null) { throw new NullPointerException("linkType"); } if (outputFile == null) { throw new NullPointerException("outputFile"); } if (objDir == null) { throw new NullPointerException("objDir"); } /** * Fully resolve version info */ VersionInfo mergedInfo = versionInfo.merge(); File versionResource = new File(objDir, "versioninfo.rc"); boolean notChanged = false; // // if the resource exists // if (versionResource.exists()) { ByteArrayOutputStream memStream = new ByteArrayOutputStream(); Writer writer = new BufferedWriter(new OutputStreamWriter(memStream)); writeResource(writer, mergedInfo, outputFile, isDebug, linkType); writer.close(); ByteArrayInputStream proposedResource = new ByteArrayInputStream( memStream.toByteArray()); InputStream existingResource = new FileInputStream(versionResource); // // // notChanged = hasSameContent(proposedResource, existingResource); existingResource.close(); } // // if the resource file did not exist or will be changed then // write the file // if (!notChanged) { Writer writer = new BufferedWriter( new OutputStreamWriter( new FileOutputStream(versionResource))); writeResource(writer, mergedInfo, outputFile, isDebug, linkType); writer.close(); } if (matcher != null) { matcher.visit(new File(versionResource.getParent()), versionResource.getName()); } } /** * Compare two input streams for duplicate content * * Naive implementation, but should not be performance issue. * @param stream1 stream * @param stream2 stream * @return true if streams are identical in content * @throws IOException if error reading streams */ private static boolean hasSameContent(final InputStream stream1, final InputStream stream2) throws IOException { int byte1 = -1; int byte2 = -1; do { byte1 = stream1.read(); byte2 = stream2.read(); } while (byte1 == byte2 && byte1 != -1); return (byte1 == byte2); } /** * Parse version string into array of four short values. * @param version String version * @return short[] four element array */ public static short[] parseVersion(final String version) { short[] values = new short[] { 0, 0, 0, 0}; if (version != null) { StringBuffer buf = new StringBuffer(version); int start = 0; for (int i = 0; i < 4; i++) { int end = version.indexOf('.', start); if (end <= 0) { end = version.length(); for (int j = end; j > start; j--) { String part = buf.substring(start, end); try { values[i] = Short.parseShort(part); break; } catch (NumberFormatException ex) { values[i] = 0; } } break; } else { String part = buf.substring(start, end); try { values[i] = Short.parseShort(part); start = end + 1; } catch (NumberFormatException ex) { break; } } } } return values; } /** * Converts parsed version information into a string representation. * * @param buf StringBuffer string buffer to receive version number * @param version short[] four-element array */ private static void encodeVersion(final StringBuffer buf, final short[] version) { for (int i = 0; i < 3; i++) { buf.append(Short.toString(version[i])); buf.append(','); } buf.append(Short.toString(version[3])); } /** * Writes windows resource. * @param writer writer, may not be nul * @param versionInfo version information * @param outputFile executable file * @param isDebug true if debug * @param linkType link type * @throws IOException if error writing resource file */ public static void writeResource(final Writer writer, final VersionInfo versionInfo, final File outputFile, final boolean isDebug, final LinkType linkType) throws IOException { //writer.write("#include \"windows.h\"\n"); writer.write("VS_VERSION_INFO VERSIONINFO\n"); StringBuffer buf = new StringBuffer("FILEVERSION "); encodeVersion(buf, parseVersion(versionInfo.getFileversion())); buf.append("\nPRODUCTVERSION "); encodeVersion(buf, parseVersion(versionInfo.getProductversion())); buf.append("\n"); writer.write(buf.toString()); buf.setLength(0); buf.append("FILEFLAGSMASK 0x1L /* VS_FF_DEBUG */"); Boolean patched = versionInfo.getPatched(); Boolean prerelease = versionInfo.getPrerelease(); if (patched != null) { buf.append(" | 0x4L /* VS_FF_PATCHED */"); } if (prerelease != null) { buf.append(" | 0x2L /* VS_FF_PRERELEASE */"); } if (versionInfo.getPrivatebuild() != null) { buf.append(" | 0x8L /* VS_FF_PRIVATEBUILD */"); } if (versionInfo.getSpecialbuild() != null) { buf.append(" | 0x20L /* VS_FF_SPECIALBUILD */"); } buf.append('\n'); writer.write(buf.toString()); buf.setLength(0); buf.append("FILEFLAGS "); if (isDebug) { buf.append("0x1L /* VS_FF_DEBUG */ | "); } if (Boolean.TRUE.equals(patched)) { buf.append("0x4L /* VS_FF_PATCHED */ | "); } if (Boolean.TRUE.equals(prerelease)) { buf.append("0x2L /* VS_FF_PRERELEASE */ | "); } if (Boolean.TRUE.equals(versionInfo.getPrivatebuild())) { buf.append("0x8L /* VS_FF_PRIVATEBUILD */ | "); } if (Boolean.TRUE.equals(versionInfo.getSpecialbuild())) { buf.append("0x20L /* VS_FF_SPECIALBUILD */ | "); } if (buf.length() > 10) { buf.setLength(buf.length() - 3); buf.append('\n'); } else { buf.append("0\n"); } writer.write(buf.toString()); buf.setLength(0); writer.write("FILEOS 0x40004 /* VOS_NT_WINDOWS32 */\nFILETYPE "); if (linkType.isExecutable()) { writer.write("0x1L /* VFT_APP */\n"); } else { if (linkType.isSharedLibrary()) { writer.write("0x2L /* VFT_DLL */\n"); } else if (linkType.isStaticLibrary()) { writer.write("0x7L /* VFT_STATIC_LIB */\n"); } else { writer.write("0x0L /* VFT_UNKNOWN */\n"); } } writer.write("FILESUBTYPE 0x0L\n"); writer.write("BEGIN\n"); writer.write("BLOCK \"StringFileInfo\"\n"); writer.write(" BEGIN\n#ifdef UNICODE\nBLOCK \"040904B0\"\n"); writer.write("#else\nBLOCK \"040904E4\"\n#endif\n"); writer.write("BEGIN\n"); if (versionInfo.getFilecomments() != null) { writer.write("VALUE \"Comments\", \""); writer.write(versionInfo.getFilecomments()); writer.write("\\0\"\n"); } if (versionInfo.getCompanyname() != null) { writer.write("VALUE \"CompanyName\", \""); writer.write(versionInfo.getCompanyname()); writer.write("\\0\"\n"); } if (versionInfo.getFiledescription() != null) { writer.write("VALUE \"FileDescription\", \""); writer.write(versionInfo.getFiledescription()); writer.write("\\0\"\n"); } if (versionInfo.getFileversion() != null) { writer.write("VALUE \"FileVersion\", \""); writer.write(versionInfo.getFileversion()); writer.write("\\0\"\n"); } String baseName = CUtil.getBasename(outputFile); String internalName = versionInfo.getInternalname(); if (internalName == null) { internalName = baseName; } writer.write("VALUE \"InternalName\", \""); writer.write(internalName); writer.write("\\0\"\n"); if (versionInfo.getLegalcopyright() != null) { writer.write("VALUE \"LegalCopyright\", \""); writer.write(versionInfo.getLegalcopyright()); writer.write("\\0\"\n"); } if (versionInfo.getLegaltrademarks() != null) { writer.write("VALUE \"LegalTrademarks\", \""); writer.write(versionInfo.getLegaltrademarks()); writer.write("\\0\"\n"); } writer.write("VALUE \"OriginalFilename\", \""); writer.write(baseName); writer.write("\\0\"\n"); if (versionInfo.getPrivatebuild() != null) { writer.write("VALUE \"PrivateBuild\", \""); writer.write(versionInfo.getPrivatebuild()); writer.write("\\0\"\n"); } if (versionInfo.getProductname() != null) { writer.write("VALUE \"ProductName\", \""); writer.write(versionInfo.getProductname()); writer.write("\\0\"\n"); } if (versionInfo.getProductversion() != null) { writer.write("VALUE \"ProductVersion\", \""); writer.write(versionInfo.getProductversion()); writer.write("\\0\"\n"); } if (versionInfo.getSpecialbuild() != null) { writer.write("VALUE \"SpecialBuild\", \""); writer.write(versionInfo.getSpecialbuild()); writer.write("\\0\"\n"); } writer.write("END\n"); writer.write("END\n"); writer.write("BLOCK \"VarFileInfo\"\n"); writer.write("BEGIN\n#ifdef UNICODE\n"); writer.write("VALUE \"Translation\", 0x409, 1200\n"); writer.write("#else\n"); writer.write("VALUE \"Translation\", 0x409, 1252\n"); writer.write("#endif\n"); writer.write("END\n"); writer.write("END\n"); } }