28
votes

I'm heavily using Cygwin (with PuTTY shell). But, it's quite tricky to invoke cl.exe (that is, the Visual C++ compiler toolchain) in the Cygwin Bash shell. Running vcvars*.bat in the Bash shell doesn't work obviously. I tried to migrate VC++'s environment variables to Cygwin, but it's not that easy.

How do I run the VC++ compiler in Cygwin's Bash shell?

12
My wrapmsvc [ code.google.com/p/wrapmsvc ] program might be helpful. It's a wrapper for calling cl.exe and other MS tools from Linux via Wine; it translates unix-style paths to Wine's windows-style paths and starts the real tool via Wine. This means you can call the compiler with unix paths without confusing the real compiler. Of course it won't work in Cygwin because there's no Wine there, but you might be able to modify it to not use Wine and instead do the path translation some other way.Ambroz Bizjak

12 Answers

25
votes

I usually solve this by adding

call "%VS80COMNTOOLS%vsvars32.bat" >NUL:

to c:/cygwin/cygwin.bat. Note that the VS80COMNTOOLS variable is extremely useful, since it gives you a foolproof (hm) way of locating vsvars32.bat.

Another approach is this, which allows you to easily switch between different Studio versions:

function run_with_bat()
{
    batfile=$1; shift
    tmpfile="$TMP/tmp$$.bat"
    echo "@echo off" > $tmpfile
    echo "call \"%$batfile%vsvars32.bat\" >NUL:" >> $tmpfile
    echo "bash -c \"%*\"" >> $tmpfile
    cmd /c `cygpath -m "$tmpfile"` "$@"
    status=$?
    rm -f $tmpfile
    return $status
}

function run_vs9()
{
    run_with_bat VS90COMNTOOLS "$@"
}

function run_vs8()
{
    run_with_bat VS80COMNTOOLS "$@"
}

You can now do:

$ run_vs8 cl
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved. 

usage: cl [ option... ] filename... [ /link linkoption... ]
$ run_vs9 cl
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

usage: cl [ option... ] filename... [ /link linkoption... ]

Note the compiler version.

15
votes

I understand that your problem is converting vcvars32.bat into a shell script.

One way around the problem is based on the idea that environment variables are inherited when one process launches another. So you can simply run vcvars32 under cmd and then run bash. This works fine on my machine:

sh-3.2$ cl
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

usage: cl [ option... ] filename... [ /link linkoption... ]

Alternatively, run set before and after invoking vcvars32 under cmd, and then create a shell script to set the environment variables.

12
votes

Following suggestions in the other answers, I did a diff of my bash "set" results with and without the MSVC variables, and came up with the following script to reproduce them for my installation of Microsoft Visual C++ 2010 Express. I've refactored it a bit to put all the installation-dependent pieces at the top, so hopefully this can be of use to others.

# These lines will be installation-dependent.
export VSINSTALLDIR='C:\Program Files\Microsoft Visual Studio 10.0\'
export WindowsSdkDir='C:\Program Files\Microsoft SDKs\Windows\v7.0A\'
export FrameworkDir='C:\WINDOWS\Microsoft.NET\Framework\'
export FrameworkVersion=v4.0.30319
export Framework35Version=v3.5

# The following should be largely installation-independent.
export VCINSTALLDIR="$VSINSTALLDIR"'VC\'
export DevEnvDir="$VSINSTALLDIR"'Common7\IDE\'

export FrameworkDIR32="$FrameworkDir"
export FrameworkVersion32="$FrameworkVersion"

export INCLUDE="${VCINSTALLDIR}INCLUDE;${WindowsSdkDir}include;"
export LIB="${VCINSTALLDIR}LIB;${WindowsSdkDir}lib;"
export LIBPATH="${FrameworkDir}${FrameworkVersion};"
export LIBPATH="${LIBPATH}${FrameworkDir}${Framework35Version};"
export LIBPATH="${LIBPATH}${VCINSTALLDIR}LIB;"

c_VSINSTALLDIR=`cygpath -ua "$VSINSTALLDIR\\\\"`
c_WindowsSdkDir=`cygpath -ua "$WindowsSdkDir\\\\"`
c_FrameworkDir=`cygpath -ua "$FrameworkDir\\\\"`

export PATH="${c_WindowsSdkDir}bin:$PATH"
export PATH="${c_WindowsSdkDir}bin/NETFX 4.0 Tools:$PATH"
export PATH="${c_VSINSTALLDIR}VC/VCPackages:$PATH"
export PATH="${c_FrameworkDir}${Framework35Version}:$PATH"
export PATH="${c_FrameworkDir}${FrameworkVersion}:$PATH"
export PATH="${c_VSINSTALLDIR}Common7/Tools:$PATH"
export PATH="${c_VSINSTALLDIR}VC/BIN:$PATH"
export PATH="${c_VSINSTALLDIR}Common7/IDE/:$PATH"
6
votes

I actually used JesperE answer https://stackoverflow.com/a/374411/380247 (that allows you to switch between VS versions) but found a way not to create temporary bat file. So result is much simpler.

function run_in_vs_env
{
    eval vssetup="\$$1\\vsvars32.bat"
    cmd /Q /C call "$vssetup" "&&" "${@:2}"
}

function run_vs11
{
    run_in_vs_env VS110COMNTOOLS "$@"
}

function run_vs10
{
    run_in_vs_env VS100COMNTOOLS "$@"
}

I suggest to add these lines to your .bash_functions or something similar and export these functions there. This will allow you to use functions in your bash scripts.

export -f run_in_vs_env
export -f run_vs11
export -f run_vs10

Then you should be able to do:

run_vs11 cl
2
votes

Another alternative is to open up vcvars32.bat (or vsvars32.bat, which does the real work in recent version of Visual Studio), look at what it does, and replicate that in the appropriate shell script.

It's not particularly complex - all it does is set a bunch of environment variables.

2
votes

I have converted my vsvars32.bat file with my visual_studio.env file. When I need to use the command-line Visual Studio's environment, I just do a source of this file.

In the sh shell environment, I cannot register the Windows path (\ and ; clash with sh) so before I translate them with cygpath -au or cygpath -aup commands then I write down them into my .env file and translate them back with cygpath -aw or cygpath -aup commands.

My visual_studio.env file look like this:

VS80COMNTOOLS=$(cygpath -aw '/cygdrive/c/Programmi/Microsoft Visual Studio 8/Common7/Tools/'); export VS80COMNTOOLS
VSINSTALLDIR=$(cygpath -aw '/cygdrive/c/Programmi/Microsoft Visual Studio 8'); export VSINSTALLDIR
VCINSTALLDIR=$(cygpath -aw '/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC'); export VCINSTALLDIR
FrameworkDir=$(cygpath -aw '/cygdrive/c/WINDOWS/Microsoft.NET/Framework'); export FrameworkDir
FrameworkVersion='v2.0.50727'; export FrameworkVersion
FrameworkSDKDir=$(cygpath -aw '/cygdrive/c/Programmi/Microsoft Visual Studio 8/SDK/v2.0'); export FrameworkSDKDir

echo Setting environment for using Microsoft Visual Studio 2005 x86 tools.

DevEnvDir=$(cygpath -aw '/cygdrive/c/Programmi/Microsoft Visual Studio 8/Common7/IDE'); export DevEnvDir

PATH='/cygdrive/c/Programmi/Microsoft Visual Studio 8/Common7/IDE:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/BIN:/cygdrive/c/Programmi/Microsoft Visual Studio 8/Common7/Tools:/cygdrive/c/Programmi/Microsoft Visual Studio 8/Common7/Tools/bin:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/PlatformSDK/bin:/cygdrive/c/Programmi/Microsoft Visual Studio 8/SDK/v2.0/bin:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/VCPackages':$PATH
INCLUDE=$(cygpath -awp '/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/ATLMFC/INCLUDE:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/INCLUDE:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/PlatformSDK/include:/cygdrive/c/Programmi/Microsoft Visual Studio 8/SDK/v2.0/include'); export INCLUDE
LIB=$(cygpath -awp '/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/ATLMFC/LIB:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/LIB:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/PlatformSDK/lib:/cygdrive/c/Programmi/Microsoft Visual Studio 8/SDK/v2.0/lib'); export LIB
LIBPATH=$(cygpath -awp '/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727:/cygdrive/c/Programmi/Microsoft Visual Studio 8/VC/ATLMFC/LIB'); export LIBPATH

I hope this can help you.

1
votes

I find it a better strategy to use msbuild and then at the same time, I can also use g++ of my cygwin environment. It is a more integrated approach and blends harmoniously with the your existing visual studio development. Using the lowest level cl and setting up environment variables is asking for trouble and going against the wind. The msbuild can better setup things for us. Also, I can step-debug into the IDE having a full-pledge *.vcxproj project.

ls -l ~/bin/msbuild
lrwxrwxrwx 1 johnny Domain Users 102 Sep  4 04:25 /home/johnny/bin/msbuild -> /cygdrive/c/Program Files (x86)/Microsoft Visual Studio/2017/Professional/MSBuild/15.0/Bin/MSBuild.exe

Then in your ~/.bashrc, have this function:

builddebug (){
    [ $# -eq 0 ] && return 1;
    msbuild /m /p:Configuration=Debug /p:Platform=Win32 "$1"
}

The feel of the development experience looks like this. At least, i'm able to use the g++ compiler and the Visual Studio 2017 compiler in a better integrated fashion. Let msbuild do its thing. Not cl.

0
votes

For me, the following in .bash_profile worked:

"`cygpath -ua "$VS80COMNTOOLS/vsvars32.bat"`" > NUL

0
votes

One utility I found pretty invaluable for compiling stuff with msvc from a cygwin environment is a wrapper I found in the Coin 3D library's source repository called "wrapmsvc", who's binary can be found here.

The program wraps cl.exe and converts any GCC args specified to the appropriate cl.exe arg. It also uses cygwin API to correctly translate the filepath from cygwin form (/cygdrive/c/temp/test.c) to it's actual file path (C:\temp\test.c).

The source took me a little while to find last time, but it's called "wrapmsvc.cpp", so if you need to compile it, look for that file. If you do happen to compile it and you get some depreciation warnings/errors about the use of cygwin_conv_to_posix_path or cygwin_conv_to_win32_path, make the following changes:

Change the line:

(void)cygwin_conv_to_posix_path(s.c_str(), buf);

to

(void)cygwin_conv_path(CCP_WIN_A_TO_POSIX, (const void *)s.c_str(), (void *)buf, (size_t)MAX_PATH);

and change:

(void)cygwin_conv_to_win32_path(s.c_str(), buf);

to

(void)cygwin_conv_path(CCP_POSIX_TO_WIN_A, (const void *)s.c_str(), (void *)buf, (size_t)MAX_PATH);
0
votes

I couldn't add comments to Diomidis's reply :(, so had to post an answer instead. I agree with his answer, but it would be tedious to do "open a command prompt", run Visual Studio/vcvars32.bat and then run Bash. If you don't bother about cluttering you environment, the best alternative is to modify Cygwin.bat and modify it to look something like this:

@echo off
D:
chdir D:\cygwin\bin
"%VS71COMNTOOLS%\vsvars32.bat" && bash --login -i

Substitute the environment variable VS71COMNTOOLS with something appropriate on your machine.

There is no need to open a command prompt and run Bash manually. I like this solution a lot :). I couldn't give credits to the real author of this hack, as I couldn't find a link back to his article, sorry about that!

0
votes

My version of Igor's solution, it's briefer:

cl() {
  tmpfile="/tmp/tmp$$.bat"
  echo "@echo off" > $tmpfile
  echo "call \"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat\" >NUL:" >> $tmpfile
  echo "bash -c \"cl %*\"" >> $tmpfile
  cmd /c `cygpath -m "$tmpfile"` "$@"
  status=$?
  rm -f $tmpfile
  return $status
}

it's a pity that vcvars64.bat needs to be called repetitively.

0
votes

None of the other answers worked for me, but this did:

  1. Run "MS Build Command Prompt"

enter image description here

  1. dump the env vars: set > c:\temp\cl.env

  2. Open a cygwin command prompt and create a source script:

awk < /cygdrive/c/temp/cl.env -F= '{ if($1 !~ ")") print "export " $1 "=\x27" $2 "\x27" }' > cl.source
  1. Modify cl.source to change TEMP and TMP to C:\Temp e.g. TEMP='C:\Temp'

  2. Now, whenever you need the cl environment, from your cygwin prompt run: source cl.source

  3. Optionally, source cl.source in your .bashrc file to run automatically when you log in