Jayson Gazeley
Jayson Gazeley

Reputation: 45

Compile C Programs from Windows Powershell with MSVC

Edit: To anyone interested in doing this, take a look at my answer below. I got this working exactly as I was hoping to.

I know this is a topic that has probably been addressed before, but I can't seem to find a straight answer in the searching I've done, everything is extremely convoluted.

I'm trying to get my Powershell set up so I can compile simple C programs directly from a Powershell terminal, and not need to use Visual Studio or the "developer console" they keep recommending.

So far I have installed the Visual Studio build tools, as I was trying to avoid downloading the entire Visual Studio IDE. I was able to compile a Hello World C program using the developer console as a test to make sure everything was working and it compiled and ran fine. However I want to be able to run cl.exe directly from Powershell without using the developer console.

I managed to add a folder to the environment variables in Windows System properties, and successfully got Powershell to recognize the cl command and show me this:

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

I feel like I'm so close here, but when I actually try to run "cl hello.c" to compile my little test program I get this error:

hello.c(1): fatal error C1034: stdio.h: no include path set

I added the folder "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\bin\Hostx86\x64" to the environment variables path section, but I noticed there are multiple cl.exe files in different folders.

Can anyone tell me how to dial this thing in so I can actually have the cl function fully functioning from Powershell? Thanks in advance for any help on this.

P.S. I have WSL installed and have multiple Raspberry Pis that I could use the gcc compiler, but that is not my aim here. I'm also aware of things like MinGW and Cygwin but am not looking to use those solutions. I am trying to get this specific scenario to work: using the cl command in Powershell without the developer console. It can't be THAT hard, and I feel like I'm so close and just need to add a few more Environment variables or something.

Here's a screenshot of me trying the cl command in PowerShell and the error it gives. https://i.sstatic.net/tu62f.jpg

Upvotes: 2

Views: 15434

Answers (3)

Jayson Gazeley
Jayson Gazeley

Reputation: 45

Ok I figured this out thanks to Chuck Walbourn, please check out his answer to see where I got all the appropriate directories to make this work. To add the environment variables permanently to your system so that typing "cl" from any PowerShell terminal will work, do the following for Windows 10 with the 2019 version of Visual Studio Build Tools (this will likely work with other variations but you will have to adjust things accordingly):

Hit Window Key and start typing "Environment Variables". You will see an option to "Edit the system environment variables". Click the "Environment Variables..." button in the bottom right of the Advanced Tab. You will see sections for "User variable names for " and "System variables". This next step assumes you don't see variables for "Include" and "Lib" in this section. Under the System variables section, hit "New..." and name the variable Include, and for the variable value, enter:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\ucrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\winrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\cppwinrt

Make sure to check these folders actually exist on your system, and adjust version numbers and "Enterprise" accordingly. I had to change "Enterprise" to "BuildTools".

Next make a new variable called "Lib" and enter the following:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\lib\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\ucrt\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\um\x86

Same rule applies, make sure to adjust these directories to match your Visual Studio install directories.

Finally, assuming the Path variable already exists which it did for me, double click "Path" and add the following keys one by one by hitting the New button in the Path menu that pops up:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\bin\HostX86\x86

C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86

C:\Program Files (x86)\Windows Kits\10\bin\x86

Tweak the directories to match your system, and if necessary the x86 vs x64 directories (I just used x86).

Here is an image highlighting some of the relevant windows and buttons mentioned in this post. Hopefully anyone messing with this stuff can figure out the minor details. Do all of this at your own risk, I have no idea if this could somehow hose your system if something gets screwed up.

If all goes well, you should be able to open PowerShell in the directory of your .c file, and type:

cl filename.c

And the system will output filename.obj and filename.exe

Success! Thanks again to Chuck for all the help, and thank you to thurizas for contributing and offering another potential solution.

Upvotes: 2

Chuck Walbourn
Chuck Walbourn

Reputation: 41057

Open up a Visual Studio Developer Command Prompt. Assuming it has defaulted to your %VSINSTALLDIR% as normal, then search for vcvars.bat in that tree.

If you look in that file, you'll see that you need to set INCLUDE, LIB, and PATH to get command-line builds to work.

In particular, the "C" portion of the C/C++ Runtime is actually in the Windows 10 SDK under:

  • c:\Program Files (x86)\Windows Kits\10\Include\<version>\ucrt for the headers
  • C:\Program Files (x86)\Windows Kits\10\Lib\<version>\ucrt\<arch> for the libraries.

See Microsoft Docs.

Because Visual Studio supports many side-by-side toolsets, the compiler and the VC Runtime and "C++" portion of the include/libs are located under: C:\Program Files (x86)\Microsoft Visual Studio\2017\<VSEdition>\VC\Tools\MSVC\<toolset> in the bin, include, and lib/<arch> folders which should be set as part of the environment variables above.

For VS 2017 (15.9) Enterprise edition using the Windows 10 SDK (17763) for example, that would be:

MS-DOS Command Prompt syntax:

set INCLUDE=C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\include;
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\ucrt;
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\shared;
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um;
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\winrt;
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\cppwinrt

The Powershell syntax is:

$env:INCLUDE = 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\ucrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\shared;C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\winrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\cppwinrt'

Assuming you are creating a 32-bit (x86) program:

MS-DOS Command Prompt:

set LIB=C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\lib\x86;
C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\ucrt\x86;
C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x86;

PowerShell:

$env:LIB = 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\lib\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\ucrt\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x86;'

This should be sufficient for classic Win32 desktop development. For C++/CX, Managed C++, and/or UWP development you would need additional directories and the LIBPATH variable set.

And you want the compiler itself to be a 32-bit (x86) EXE:

MS-DOS Command Prompt:

set PATH=C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\bin\HostX86\x86;
C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86;
C:\Program Files (x86)\Windows Kits\10\bin\x86;
%PATH%

PowerShell:

$env:PATH='C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\bin\HostX86\x86;C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86;C:\Program Files (x86)\Windows Kits\10\bin\x86;' + $env:PATH

VS 2019 16.11.3 w/ Windows SDK (19041)

$env:INCLUDE = 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\ucrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\winrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\cppwinrt'
$env:LIB = 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\lib\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\ucrt\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\um\x86;'
$env:PATH='C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\bin\HostX86\x86;C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86;C:\Program Files (x86)\Windows Kits\10\bin\x86;' + $env:PATH

Change Enterprise to the appropriate SKU: Community, Professional, Enterprise, or BuildTools

Upvotes: 2

thurizas
thurizas

Reputation: 2508

The best answer would be to use the developer prompt that comes with Visual Studio, as it sets all the environemental variables needed by the tools. However, if you insist to do everything by hand, here is what I needed to do.

Opening a command prompt on my windows system and typing cl I get cl is not recognized as an internal or external command. The issue here is that cl.exe is not on my path. I can modify the path environmental variable so that the shell can find cl.exe. On my system this is accomplished by doing

>set PATH="C:\VS2019\VC\Tools\MSVC\14.29.30037\bin\Hostx64\x64";%PATH%

which will add the path to cl to my PATH. Note that this only works on the terminal that I am working in. Once this works, you can set it through the environmental variables so it is persisted and available in all command prompts.

Once, cl is in my path I can attempt to compile the following simple program:

#include <stdio.h>
#include <stdlib.>

int main(int argc, char** argv)
{
    printf("Hello World\n");
    return 0;
}

And I am greeted with C1034: stdio.h no include path set. This means that although the compiler has a built-in set of directories that it looks for include files, it can't find stdio.h. The solution is that I need to tell it where to look.

Now on my system I can find it at "C:\Windows Kits\10\Include\10.0.190410\ucrt". (Paths will be specific to my installation, you will need to find each file on your system).

Now I can tell the compiler where to find that file by using the /I (include search path) option [1]. Executing the following command

cl /I"C:\Windows Kits\10\Include\10.0.19041.0\ucrt hello.c

Gives an error "C1083: Cannot open include file: 'vcruntime.h'. Another search of my hard-drive reveals that file to be at "C:\VS2019\VC\Tools\MSVC\14.29.30037\include", and executing the following command

cl /I"C:\Windows Kits\10\Include\10.0.19041.0\ucrt"/I"C:\VS2019\VC\Tools\MSVC\14.29.30037\include" hello.c

Expanding the above command a bit to just do a compilation we can use the following

cl /c /I"C:\Windows Kits\10\Include\10.0.19041.0\ucrt"/I"C:\VS2019\VC\Tools\MSVC\14.29.30037\include" hello.c /Fo hello.o

To produce the object file hello.o.

Now on to linking. The Microsoft linker is called LINK[2] and starting with

LINK hello.o /OUT:hello.exe

Gives an error "LNK1104: cannot open file 'LIBCMT.lib'. Well this is progress, I gotten through the compilation phase and into the link phase, I only need to tell the compiler where to find the library.

Yet another search of my hard drive shows the library to be at "C:\VS2019\VC\Tools\MSVC\14.29.30037\lib\x64", and running the following command,

LINK hello.o /LIBPATH:"C:\VS2019\VC\Tools\MSVC\14.29.30037\lib\x64" /OUT:hello.exe

Gives the error LNK1104: cannot open file "kernel32.lib", which I found at "C:\Windows Kits\10\lib\10.0.19041.0\um\x64" I also had to add "C:\Windows Kits\10\lib\10.0.19041.0\ucrt\x64", to resolve libucrt.lib". The following command

LINK hello.o /LIBPATH:"C:\VS2019\VC\Tools\MSVC\14.29.30037\lib\x64" /LIBPATH:"C:\Windows Kits\10\lib\10.0.19041.0\um\x64" /LIBPATH:"C:\Windows Kits\10\lib\10.0.19041.0\ucrt\x64" /OUT:hello.exe    

Finally produce the output file hello.exe, which can as expected from a command prompt.

Remember that the above was for a simple hello-world style application. As your program gets bigger, the various paths you will need to specify will get larger as well. You may want to look into using nmake and using makefiles to manage you build.

[1] you can use "cl /?" to see all command line options available with the Microsoft compiler.

[2] you can use "LINK /?" to see all command line options available with the Microsoft linker.

Upvotes: 0

Related Questions