Say I have some C++ project which builds an exe or dll file. The project is checked into a SVN repository. I want to automatically synchronize the revision from SVN with the version resource embedded in my exe/dll file, i.e. the version should be something like $major.$minor.$svn_revision.
Any ideas on how to achieve this? Are there any out-of-the-box solutions available?
问题:
回答1:
If you have TortoiseSVN installed, then there is a program installed with it, SubWCRev
.
If, in your file, you have this value:
$WCREV$
Then it'll be replaced with the highest committed revision number if you execute something like this:
SubWCRev .\ yourfile.txt.template yourfile.txt
This will copy from yourfile.txt.template
, do the substitutions, and write to yourfile.txt
.
Note that there's a lot of other macros you can use as well, if you execute SubWCRev
without any arguments, it'll list them all on the console.
回答2:
This is great help, thanks. I've refined this for Visual Studio 2008 if it's of any help to anyone.
1/ Created a /Build folder within each project
2/ Copied AssemblyInfo.cs to the Build folder as AssemblyInfo.cs.txt, set the Build Action to "None"
3/ Edited the AssemblyInfo.cs.txt to have version attributes as below:
[assembly: AssemblyVersion("2.0.0.$WCREV$")]
[assembly: AssemblyFileVersion("2.0.0.$WCREV$")]
4/ Added the following to the Prebuild events:
SubWCRev $(SolutionDir) $(ProjectDir)\Build\AssemblyInfo.cs.txt $(ProjectDir)\Properties\AssemblyInfo.cs
This works everytime you compile.
I am using VisualSVN/TortoiseSVN and VisualSVN Server with Visual Studio 2008.
UPDATE:
My colleague has just updated his working copy and AssemblyInfo.cs is conflicted. Seems obvious. I have excluded it from SVN using VisualSVN to resolve this.
回答3:
The answer by Program.X works great. I'd like to add, though, that if you build your executable before committing your changes, the revision will be one less than the actual revision number of the running code. To mitigate this, you can set the versions to
"2.0.$WCREV$.$WCMODS?1:0$"
That will put a 1 on the end if there are any local modifications, and 0 if not. So if you look at your executable later and see 2.0.54.1, you know it's probably actually revision 55.
回答4:
You might want to look into Subversion Properties and Subversion Keywords. They don't solve the resource problem since they always include that damned $KeywordName: ...$
part. Custom properties do provide a nice method for making metadata available in batch files and what not.
Anyway, I looked for a solution to the resource problem a few years ago and didn't find one. So, we created our own home-grown solution. We changed our RC file to include a header file that was generated during the build process. The RC was dependent on the header and the header had a custom build rule that invoked a batch file to generate the header. The following snippet will extract the current revision from the output of svn info
.
SET rootdir=%1
SET svnrev=0
PUSHD "%rootdir%"
FOR /F "tokens=1-4 delims=: " %%I IN ('svn info') DO (
IF /I {%%I}=={rev} SET svnrev=%%L
)
(ECHO./*
ECHO. * version-stamp.h - repository version information
ECHO. */
ECHO.#ifndef VERSION_STAMP_H
ECHO.#define VERSION_STAMP_H
ECHO.#define REPOSITORY_VERSION %svnrev%
ECHO.#endif) > include\version-stamp.h
POPD
Then we created a component specific version stamping header named component-info.h
that looked something like:
#ifndef component_info_h
#define component_info_h
#include "product-info.h"
#include "version-stamp.h"
#define VERS_MAJOR 1
#define VERS_MINOR 2
#define VERS_PATCH 3
#define VERS_BUILD REPOSITORY_VERSION
#define MY_COMPONENT_NAME "TPS Report Generator"
#define MY_VERSION_NUMBER VERS_MAJOR,VERS_MINOR,VERS_PATCH,VERS_BUILD
#define MY_VERSION_STRING VERSION_STRING(VERS_MAJOR,VERS_MINOR,VERS_PATCH,VERS_BUILD)
#endif
Finally, we had a product-line version file that defined the product information named product-info.h
:
#ifndef product_info_h
#define product_info_h
#define PROD_VERS_MAJOR 0
#define PROD_VERS_MINOR 1
#define PROD_VERS_PATCH 0
#define PROD_VERS_BUILD 0
#define VSTR1(s) #s
#define VSTR(s) VSTR1(s)
#define VERSION_STRING(a,b,c,d) VSTR(a) "." VSTR(b) "." VSTR(c) "." VSTR(d) "\0"
#define MY_COMPANY_NAME "IniTech\0"
#define MY_COPYRIGHT "Copyright ©2009 " MY_COMPANY_NAME
#define MY_PRODUCT_NAME "\0"
#define MY_PRODUCT_VERSION_NUM PROD_VERS_MAJOR,PROD_VERS_MINOR,PROD_VERS_PATCH,PROD_VERS_BUILD
#define MY_PRODUCT_VERSION_STR VERSION_STRING(PROD_VERS_MAJOR,PROD_VERS_MINOR,PROD_VERS_PATCH,PROD_VERS_BUILD)
#endif
Then your resource file includes component-info.h
and uses the various defines in the appropriate places (e.g., FILEVERSION MY_VERSION_NUMBER
). This structure gave us a lot of flexibility and traceability in the whole version stamping process. It grew from a simple chunk in a batch file into this multi-leveled monstrosity but it has worked very well for us for the last few years.
I find it hard to believe that no one has found a better way to do this yet. Then again, I haven't investigated it for a number of years. I would assume that you could add a custom .rules
file that defines a custom tool that handles this.