How To: Build examples using Visual Studio

Comments, questions, bug reports, etc.
Post Reply
don
Posts: 9
Joined: Fri Nov 11, 2016 7:13 pm

How To: Build examples using Visual Studio

Post by don »

It's been fun figuring out how to build the wimlib examples using Visual Studio so I thought I'd share. Here are the high level tasks that must be performed as of wimlib 1.10.0:

- Build wimlib from source (both i686 and x86_64 architectures).
- Create a new Win32 Console Application using Visual Studio 2013/2015.
- Add support for wimlib.
- Copy example and tweak main and wimlib_get_error_string() param.

Let's go!

1. To build wimlib from source, follow the instructions in README.WINDOWS.

Wait! Why am I building wimlib when all I want to do is simply reference the libwim-15.dll that ships in the release package? Because we need to tell the linker how to resolve external symbols, such as _wimlib_add_image. Without it, you will receive build errors like this:

error LNK2019: unresolved external symbol _wimlib_add_image referenced in function _wmain

We can use libwim.dll.a which is the archive that contains symbol definitions for library. This is not currently included in the release package so we need to build it. Remember to build for each (Windows relevant) architecture and keep a copy of each handy.

2. Create a new Win32 Console Application using Visual Studio 2013/2015.

FILE > New Project...
Templates > Visual C++ > Win32 > Win32 Console Application
Accept all wizard defaults.

Add x64 build support to our project:
BUILD > Configuration Manager > Active solution platform: > New > x64

3. Add support for wimlib.

Create two directories in the new console project directory (as peers to the .vcxproj and other files)
\i686
\x86_64
Copy the builds of libwim.dll.a generated in step 1 into each appropriate directory.

Now we need to tell the linker about these files, and align them with the build architectures.
PROJECT > Properties... > Platform > Win32
PROJECT > Configuration Properties > Linker > General > Additional Library Directories > .\i686
PROJECT > Configuration Properties > Linker > Input > Additional Dependencies > add libwim.dll.a to the list
OK
PROJECT > Properties... > Platform > x64
PROJECT > Configuration Properties > Linker > General > Additional Library Directories > .\x86_64
PROJECT > Configuration Properties > Linker > Input > Additional Dependencies > add libwim.dll.a to the list
OK
Note: When adding additional library dependencies, be sure to include a semicolon before and after. For example, the end of the dependency list should look something like ...odbccp32.lib;libwim.dll.a;%(AdditionalDependencies)

Copy wimlib.h from the wimlib source \include directory to the new console project directory (as a peer to the .vcxproj and other files). There are currently some nuances depending on what version of Visual Studio you use as discussed here.

Copy libwim-15.dll of each architecture to the corresponding Windows directory. For example:

wimlib-1.10.0-windows-i686-bin > WINDOWS\SysWOW64
wimlib-1.10.0-windows-x86_64-bin > WINDOWS\System32

4. Copy example and tweak main and wimlib_get_error_string() param.

Copy the contents of \examples\capturewim.c into your new console project's main .cpp file, overwriting all but the existing #include "stdafx.h" line at the top.

Modify main to support Microsoft's implementation of unicode/non-unicode builds:

From
int main(int argc, char **argv)
to
int _tmain(int argc, char* argv[])

If you don't, you'll get wimlib error 47 (file open) since the command line parameters will be passed into the functions incorrectly.

Cast ret to wimlib_error_code so wimlib_get_error_string doesn't complain:

From
wimlib_get_error_string(ret));
to
wimlib_get_error_string((wimlib_error_code)ret));

Here is the updated example:

Code: Select all

#include "stdafx.h"
#include "wimlib.h"
#include <stdio.h>

#define TO_PERCENT(numerator, denominator) \
	((float)(((denominator) == 0) ? 0 : ((numerator) * 100 / (float)(denominator))))

static enum wimlib_progress_status
write_progress(enum wimlib_progress_msg msg,
	union wimlib_progress_info *info, void *progctx)
{
	switch (msg) {
	case WIMLIB_PROGRESS_MSG_WRITE_STREAMS:
		printf("Writing WIM: %.2f%% complete\n",
			TO_PERCENT(info->write_streams.completed_bytes,
				info->write_streams.total_bytes));
		break;
	default:
		break;
	}
	return WIMLIB_PROGRESS_STATUS_CONTINUE;
}

// int main(int argc, char **argv)
int _tmain(int argc, char* argv[])
{
	int ret;
	WIMStruct *wim = NULL;
	const char *srcdir;
	const char *wimpath;

	/* Check for the correct number of arguments.  */
	if (argc != 3) {
		fprintf(stderr, "Usage: capturewim DIR WIM\n");
		return 2;
	}

	srcdir = argv[1];
	wimpath = argv[2];

	/* Create a WIMStruct for a WIM.  */
	ret = wimlib_create_new_wim(WIMLIB_COMPRESSION_TYPE_LZX, &wim);
	if (ret != 0)  /* Always should check the error codes.  */
		goto out;

	/* Register our progress function.  */
	wimlib_register_progress_function(wim, write_progress, NULL);

	/* Add the directory tree to the WIMStruct as an image.  */

	ret = wimlib_add_image(wim,     /* WIMStruct to which to add the image    */
		srcdir,  /* Directory from which to add the image  */
		NULL,    /* Name to give the image (NULL means none)  */
		NULL,    /* Capture configuration structure (NULL means none)  */
		0);      /* WIMLIB_ADD_FLAG_* flags (0 means all defaults)  */
	if (ret != 0)
		goto out;

	/* Write the WIM file.  */

	ret = wimlib_write(wim,      /* WIMStruct from which to write a WIM  */
		wimpath,  /* Path to write the WIM to             */
		WIMLIB_ALL_IMAGES, /*  Image(s) in the WIM to write */
		0,        /* WIMLIB_WRITE_FLAG_* flags (0 means all defaults)   */
		0);       /* Number of compressor threads (0 means default)  */

out:
	/* Free the WIMStruct.  Has no effect if the pointer to it is NULL.  */
	wimlib_free(wim);

	/* Check for error status.  */
	if (ret != 0) {
		fprintf(stderr, "wimlib error %d: %s\n",
			ret, wimlib_get_error_string((wimlib_error_code)ret));
	}

	/* Free global memory (optional).  */
	wimlib_global_cleanup();

	return ret;
}
Enjoy!
synchronicity
Site Admin
Posts: 474
Joined: Sun Aug 02, 2015 10:31 pm

Re: How To: Build examples using Visual Studio

Post by synchronicity »

Glad to hear you got it working! I think you're one of the first people to actually try to compile this stuff with Visual Studio. Most previous Windows users have used the DLL though LoadLibrary(), or the equivalent for whatever programming language they happen to be using. I should be able to address some of these issues going forwards:

a. It looks like wimlib's build process already produces an import library (libwim.dll.a). I'll consider bundling this and wimlib.h into the release ZIP files.
b. As mentioned on the other thread I've written a patch to wimlib.h for the struct timespec issue.
c. The example programs actually have two problems: lack of C++ compatibility and lack of Windows compatibility. Note that the string type on Windows needs to be wchar_t (char is not correct), and the programs need to be compiled in Unicode mode. I've attempted to fix these problems with all the programs, but they still need to be tested with Visual Studio and not just MinGW. For now my work is in the git repository on branch "dev".
don
Posts: 9
Joined: Fri Nov 11, 2016 7:13 pm

Re: How To: Build examples using Visual Studio

Post by don »

That's great news. Not sure if you want to add a stretch goal while you're in there, but thought I would mention it...

The import library (libwim.dll.a) has been working great so far in using libwim-15.dll dynamically. Let's say for a second you would like to use Visual Studio to create a standalone Win32 .EXE where dependencies are statically linked. In theory you would:

1. Statically link the VC runtime library:
- PROJECT > Configuration Properties > C/C++ > Code Generation > Runtim Library > /MT
2. Statically link the wimlib library
- Edit \tools\make-windows-release, remove --disable-static and build from source
- Copy the generated .libs\libwim.a static library to your Visual Studio project
- PROJECT > Configuration Properties > Linker > Input > Additional Dependencies > add libwim.a to the list
3. Build!

However you end up with ~110 linker errors such as:

Error LNK2001 unresolved external symbol ___mingw_vwprintf libwim.a(libwim_la-header.o)
Error LNK2001 unresolved external symbol ___umoddi3 libwim.a(libwim_la-write.o)

Linking to additional windeps, such as libwinpthread.a and libxml2.a resolves a number of linker errors, however this feels weird as those should be "in" libwim.a given it's statically linked to those. It feels even weirder linking to libgcc.a to help resolve some of the "core" functions. All of this seems like it's something that can be resolved in the build process of libwim itself. Like maybe a cross compiler, linker, or target flag. A quick search of "mingw static library visual studio" is entertaining but inconclusive.

Anyway, thanks again for replying.

-Don
synchronicity
Site Admin
Posts: 474
Joined: Sun Aug 02, 2015 10:31 pm

Re: How To: Build examples using Visual Studio

Post by synchronicity »

When statically linking to libraries it's the norm to have to explicitly link to all the dependencies.

Also note that if the application is proprietary, wimlib cannot be statically linked to it because that would violate the LGPL, which requires end users to be able to update the LGPL library.
synchronicity
Site Admin
Posts: 474
Joined: Sun Aug 02, 2015 10:31 pm

Re: How To: Build examples using Visual Studio

Post by synchronicity »

I've made all the changes I mentioned --- updates to wimlib.h and the example programs for MSVC compatibility, and including the import library (libwim.lib) and header (wimlib.h) in the Windows binary releases, in the "devel" directory. If you have time, please try wimlib-1.11.0-BETA1 from the downloads section to make sure it works for you. Thanks!
don
Posts: 9
Joined: Fri Nov 11, 2016 7:13 pm

Re: How To: Build examples using Visual Studio

Post by don »

As requested I tested building the capturewim example using Visual Studio 2015 and the latest wimlib (wimlib-1.11.0-BETA2). Tested both architectures. Worked great!

Here are the relevant files from the release and what I did with them:

Common
\devel\wimlib.h -> \[console_project]

i686
libwim-15.dll -> \Windows\SysWOW64
\devel\libwim.lib -> \[console_project]\i686

x86_64
libwim-15.dll -> \Windows\System32
\devel\libwim.lib -> \[console_project]\x86_64

The work you did addressed all of the challenges from this thread (main/wmain, timespec, unresolved externals).

Great job!

Don
Post Reply