Setting DOS Environment Variable to another Environment Variable in if statement - if-statement

This seems so simple, but has turned out to be such a pain.
On Windows 7, I can paste the below into a command prompt and have it set ProgramFiles(x32) to either %programfiles% or %programfiles(x86)%:
%comspec% /c if exist "%programfiles%" (set "ProgramFiles(x32)=%programfiles%") else (set %ProgramFiles(x32)=%programfiles(x86)%")
However on Windows XP, using the the same and others methods, I cannot get the same result:
E:\>%comspec% /c if exist "%programfiles%" (set "ProgramFiles(x32)=%programfiles%") else (set "ProgramFiles(x32)=%programfiles(x86)%")
E:\Program was unexpected at this time.
E:\>%comspec% /c if exist "%programfiles%" (set ProgramFiles(x32)=%programfiles%) else (set ProgramFiles(x32)=%programfiles(x86)%)
E:\Program was unexpected at this time.
E:\>%comspec% /c if exist "%programfiles%" (set "ProgramFiles(x32)"="%programfiles%") else (set "ProgramFiles(x32)=%programfiles(x86)%")
E:\Program was unexpected at this time.
E:\>%comspec% /c if exist "%programfiles%" (set ProgramFiles(x32)="%programfiles%") else (set ProgramFiles(x32)=%programfiles(x86)%)
"E:\Program Files") was unexpected at this time.
E:\>%comspec% /c if exist "%programfiles%" (set ProgramFiles(x32)=%%programfiles%%) else (set ProgramFiles(x32)=%programfiles(x86)%)
%E:\Program was unexpected at this time.
set ProgramFiles(x32)=%programfiles% works fine straight from the command line on both Win XP & 7.
What AM I missing?
As a side note, I REALLY wish Microsoft would have made %ProgramFiles% be the directory for 32 bit programs. I can't see the logic in having to do this checking, when it were that way a 32 bit program would be in the same location regardless of the bitage of the OS.

You're really better off programming in almost anything but .bat file syntax. IMHO.
But "what you're missing" is that .bat syntax considers "Program" and "Files" two separate words (instead of one part of a path).
Simple workaround:
cd c:\
dir/x pro*
In your case, you'll probably have a "progra~1" (for "Program files") and "progra~2" (for "program files (x86)". Just modify your .bat file accordingly :)

#dbenham Very in depth, and very much appreciated enlightenment in another question of mine, solves this one as well - Different behaviour of DOS Environment setting, in if statement, in DOS prompt as compared to Windows Shortcut

Related

msbuild doesn't copy output of referenced native project to c# project out dir

I'm struggling with this for a long time now.
The setup:
c# project
c++ project
c# project has a reference for the c++ project with the following lines:
<ProjectReference Include="projectB.vcxproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Content</OutputItemType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ProjectReference>
This works from withing visual-studio.
This works when using devenv from command line.
When using msbuild from command line - the output file of the c++ project is not copied over into the output directory of the c# project.
I wasn't able to fix that using msbuild. Read a lot about it, nothing worked. Tried to debug in using diag verbosity - but logs of msbuild and visual-studio are very different...
I can't turn to using devenv as the build machine doesn't have valid visual-studio.
In msbuild log with diagnostic verbosity I see:
Target "GetCopyToOutputDirectoryItems" skipped. Previously built successfully.
This is where in the visual-studio log - it looks different - and actually works on copying the referenced native files to the c# output directory.
Perhaps something related with build order?..
In msbuild log - I also see:
Target "_CopyOutOfDateSourceItemsToOutputDirectoryAlways" skipped, due to false condition; ( '#(_SourceItemsToCopyToOutputDirectoryAlways)' != '' ) was evaluated as ( '' != '' ).
While in the visual-studio build log I see this target executed (it comes right after GetCopyToOutputDirectoryItems target)
Update 3:
It seems that previous solutions cause unwanted side-effects such as breaking the build when running multi-threaded builds.
Current solution, that does seem to work is to add:
<Targets>Build;BuiltProjectOutputGroup</Targets>
to the ProjectReference section.
Update 2:
Changing:
Targets="%(_MSBuildProjectReferenceExistent.Targets)"
to
Targets="%(_MSBuildProjectReferenceExistent.Targets);GetTargetPath"
in C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets, in the MSBuild task preceded by the comment Build referenced projects when building from the command line. - did the trick.
However, I have no confidence in this solution, as I don't understand the entire build process. This is just a guess.
Update 1:
using /p:DesignTimeBuild=true affects dependency build order. Can't work. Continue investigation...
Possible solution 1:
After putting a lot of messaged into C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets
I finally got to the following:
QUIRKING FOR DEV10
I'm still not exactly sure what that is all about, and I saw that the developers plan to remove this quirk (see https://github.com/Microsoft/msbuild/issues/1890).
Suddenly DesignTimeBuild caught my eye in the following line:
<Output TaskParameter="TargetOutputs" ItemName="_ResolvedProjectReferencePaths" Condition="'%(_MSBuildProjectReferenceExistent.ReferenceOutputAssembly)'=='true' or '$(DesignTimeBuild)' == 'true'"/>
I know that inside visual-studio this work. Googling got me to https://github.com/Microsoft/msbuild/wiki/MSBuild-Tips-&-Tricks.
From there the path was short to adding /p:DesignTimeBuild=true to the msbuild command line.
That made it work. The referenced assembly was copied over.
I don't think this should be the solution, but it works, and don't seem to break anything else (yet).
Any other suggestions would be welcome.

Start windows batch from C++ CreateProcess has different behavior

Environment: windows 10, VS2013.
I have a C++ app, using Poco framework (Poco 1.7.6) and I need to launch some batch files. It works without problem, but for a particular script, and I can't figure out the reason.
This particular script is as follows (let's call it buildMySolution.bat):
set BUILD_DIR=%~dp0
call "C:\Program Files(x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"
msbuild /p:Platform=%~1 /p:Configuration=%~2 %BUILD_DIR%\Mysolution.sln
As you can see the batch file simply compiles a VS2013 solution. Needless to say that this simple batch works perfectly well if launched from command line.
The problem is that this batch is in drive D: (in D:\DevRoot\build\MySolution) and when launched from my app (in D:\ drive as well), I get a "cannot find the path" on the second line.
I tried several modifications in the batch: like forcing C: or cd /D C: ... it can go to C: but not further, it refuses to cd to the directory containing vcvarsall.bat (again, I know the path is correct as the very same script executes fine from command line). It has however no problem coming back to initial directory through a cd /D %BUILD_DIR%.
To launch the script from my C++ app, I use this:
Poco::ProcessHandle handleBuild = Poco::Process::launch(path_to_script, argsBuild);
handleBuild.wait();
The Poco launch is just a thin wrapper around CreateProcessA(), I don't see anything special in their code (Poco Process.cpp).
I tried as well to specify the working directory to be the directory containing vcvarsall.bat, but then CreateProcess fails.
I just found a solution: I changed the line (in the batch buildMySolution.bat):
call "C:\Program Files(x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"
to:
call C:\PROGRA~2\micros~1.0\vc\vcvarsall.bat
Believe it or not: using DOS names and removing quotes makes it work!!!
Bug or feature, I'm not sure...

How can I capture the cl.exe command line in Visual Studio 2010?

I have a project that I converted from a makefile that has a source file that expects the command line options from the compiler. For example for when the project was built with gcc if you did program --help it would spit out the gcc command line used to compile the program.
How can I do the same thing in Visual Studio, so that it spits out the cl command line used to compile the program? Basically I want to hit F7 (to build solution) and have the whole thing automated. I can't find a macro for it. Thanks
edit; I mean programatically, so for example I want when I run the program for its output to contain the cl.exe command string that is used. You can see the command line at Configuration Properties > C/C++ > Command Line > All Options but I can't find a macro for it or some way to encapsulate it in a file.
Since VS switched the underlying build system to MsBuild the command line as shown in that dialog is created programatically within VS only. It might not even be the exact command line passed to cl: MsBuild itself invokes CL via a task and as such there is no direct link with what is shown in VS nor is there a way to get the command line out of it.
Anyway, there is no such thing as the command line since each source file might have different options. Furthermore I doubt you want the full commandline including the absolute include paths etc. Nobody is interested in that. Now if you make clever use of the macros from http://msdn.microsoft.com/en-us/library/b0084kay.aspx you can sort of recreate the command line yourself since most options are there:
std::string CompilerCommandLineOptions()
{
std::string cmd;
#ifdef _CHAR_UNSIGNED
cmd += " /J";
#endif
#ifdef __cplusplus_cli
cmd += " /clr";
#endif
#ifdef _CPPRTTI
cmd += " /GR"
#endif
//etc
return cmd;
}
Note: are you sure it's worth the hassle? Is there really somebody interested in the command line? It's not even sufficient to build the project. Why not the linker options as well then?
A .vcxproj is a Visual Studio project in the MSBuild project format. You can build it by running msbuild.exe, devenv.exe, devenv.com, using the Visual Studio GUI or the MSBuild API.
Visual Studio GUI uses the MSBuild API. In doing so, it limits the MSBuild output.
If you want more details, change your user settings in Visual Studio:
Tools > Options > Project and Solutions > Build and Run > two verbosity settings
Detailed will show the cl.exe command lines.
The closest thing which I came across cl command line which msuild executes is "hacking" the rsp file used while calling cl.exe.
Using Override compiler solution, I changed ClCompile ToolExe to custom mycl.bat script and this script received an argument which was #tmp-1234xxx.rsp file. This rsp file contained whole command line except cl.exe path, something like -
rsp file
/P /DDEBUG Source.cpp
Then after making desired changes in the rsp file by calling a separate bash script which were very minor for me, I called cl.exe with contents of my rsp file. So, whenever user hits the build button, this script executes.
mycl.bat script
#echo off
SET PATH=%PATH%;/usr/bin //to call cygwin bash
set parameter=%1
set parameter=%parameter:~1% //to remove # in the beginning
c:/cygwin/bin/bash process.sh %parameter%
process.sh
iconv -f UCS-2 -t UTF-8 <$1 >$1.conv //file converted to UTF-8, else bash wasn't handling it well
contents=`cat $1.conv`
#Processing on file contents here
path/to/cl.exe $(contents)
Very nasty solution, but it worked for my use case. I wanted to change the names of the file on the go based on some logic.
The problem I faced is Visual Studio uses tlogs written by CL Task to check while file needs to to be rebuilt on incremental build and my target's tlogs files were not enough. There were tlogs of every command in batch and bash scripts but not for whole target. So, it was building whole thing on incremental builds also.

'cmake' is not recognised as an internal or external command

I'm trying to run cmake in Visual Studio 10, for esys-particle-win.
My path to cmake:C:\Program Files (x86)\CMake 2.8\bin\cmake.exe
My path to esys-particle-win:C:\esys-particle-win\trunk\buildvs2010\mkvs10.bat
The commands I'm typing in the administrator command prompt of Visual Studio 2010 are:
cd c:\esys-particle-win\trunk\buildvs2010
mkvs10.bat
and I'm getting this error:
'cmake' is not recognized as an internal or external command
contents of mkvs10.bat:
cmake .. -G "Visual Studio 10" -G "NMake Makefiles"
could anyone tell me where I am wrong?. I don't know computer programming. I followed the instructions mentioned in section 2.3.1 of this site: `
https://launchpadlibrarian.net/139659869/esys-particle-win-%28v2.1%29-build-instructions.pdf
`
Any help would be greatly appreciated, Thank you.
The error message means it cannot find cmake.
You can add its location to your path from the prompt like this:
set PATH="C:\Program Files (x86)\CMake 2.8\bin\";%PATH%
As #doctorlove mentioned above, the error message means it cannont find Cmake.
Note that quotes aren't needed in PATH environmental variables on Windows. So the above example on Windows would look like:
set PATH=C:\Program Files (x86)\CMake 2.8\bin\;%PATH%
I had the same issue, and resolved it in this post.
note that if you installed cmake via chocolatey, you may have neglected to add the argument --installargs 'ADD_CMAKE_TO_PATH=System'. If you've already choco-installed cmake without that argument, re-installing via --force won't respect the new argument: you'll need to uninstall and then install. specifically choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System'
I had the same problem since I intalled CMake in D:\Program Files , I fixed it by manually adding a path variable.
Open control panel
Go to System and Security then go to System.
How it looks like in after step 2
Here Select advanced System settings, a dialogue box will appear.
The dialogue box
Now go to Environment Variables.
Now select path and then click on edit
After the 4th Step
Here add a new path at the bottom of many pre existing paths.
In my case i installed CMake in D:\Program Files\
So I need to add path D:\Program Files\CMake\bin. You should copy the path to your CMake folder and add \bin at the end.
Now open you have to restart command prompt to see the changes.
I found the CMake to be:
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin
I added it to the User PATH as described above, by hrithik singla, and node-gyp worked, specifically "npm install". I expect it will change again in the future. So the way I found it was by having Windows Explorer search "C:\Program Files (x86)\Microsoft Visual Studio\2019" and then dig through the results for the CMake path. Probably, other development tools will install CMake to different folders.
I'm trying to build a project with my recently downloaded Visual Studio Community 2017, but had no CMake on my path.
It did not help, even after I had gained VCVars: "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
Instead of separately installing a copy that might work with these answers, although I'm not sure it would have the generators I need(?), I found one in the installation directory, which had a different path than what was in the guide I was using.
Here is my invocation line: "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -G "Visual Studio 15 2017" -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE% ..
There are a few issues that can cause this. And it's mostly windows related. This is more on the cmake side of things, but it addresses a few windows specific problems you may encounter using CMake with Windows. This is fresh in my head, and this popped up, so I'll drop this here. Here we go.
1. CMake will separate a variable to list if there are spaces in the path.
If you are calling another instance of CMake from within CMake, Sending a Program Files path will slice those strings, and divide your variable into a 3 item list. The spaces will be replace by a semicolon divider.
set(CMAKE_EXE C:\Program Files (x86)\CMake\bin\cmake.exe)
"C:\Program;Files;(x86)\CMake\bin\cmake.exe <- CMAKE_EXE is now a 3 item list separated by ; "
list(LENGTH ${CMAKE_EXE} count)
message("CMAKE_EXE has ${count} items") "-> displays 3"
On Windows, All path variables should be enclosed in quotations to infer that they are 1 single string variable. Not just for cmake, but for batch scripting, basic command line etc.
set(CMAKE_EXE "C:/Program Files (x86)/CMake/bin/cmake.exe")
Now, any time you reference CMAKE_EXE you'll need to always keep it enclosed in quotations, becuase cmake WILL break it to a list again otherwise.
execute_command(COMMAND cmd /c ${CMAKE_EXE} -P myScript.cmake) <-- BAD
execute_command(COMMAND cmd /c "${CMAKE_EXE}" -P myScript.cmake) <- GOOD
Just get in the habit of always putting quotations around paths you reference.
2. Stay away from the Windows back slashes!.
Windows uses back slashes by default for it's path divider, which are escape sequences in most coding languages, including CMake. Just send windows / forward slashes instead. This eliminates any headaches you'll have with doubling up escape characters in string literals to match the path. \
And remember, windows is always gonna try to give you paths in \ format. Windows likes backslashes in certain places like environment paths, and settings files, while cmake likes forward slashes. You need at some point to convert between the different formats.
Use something like this to convert the path to be more cross platform compatible. You can replace "in place" on your existing variable.
"CMAKE_EXE = C:\Program Files (x86)\CMake\bin\cmake.exe <- value before"
string(REPLACE "\\" "/" CMAKE_EXE "${CMAKE_EXE}") "<- notice the quotes again"
"CMAKE_EXE = C:/Program Files (x86)/CMake/bin/cmake.exe <- value after"
Take a look at these CMake functions designed to do path conversions.
https://cmake.org/cmake/help/latest/command/file.html#to-native-path
3. Sometimes, Windows interprets unquoted paths as 8.3SFN (8DOT3) format
8.3 filename
Back in the days of MSDOS and Windows 95, we dealt with the FAT file system and 8.3Short Filenames. The command prompt could not work with more than 8 character filenames so we needed a way to access long windows filenames before quotation string support. 8 characters + 3 for the extension. And most systems still support 8.3 today. Here's an example.
C:\Program Files\Windows\System32\Calc.exe <- \Program Files\ is 13 characters
in order to CD into this path without quotes, you have to use the short path. like so.
CD C:\Progra~1\Windows\System32\Calc.exe <-- *Progra~1 is 8 characters, 1st occurrence.*
You just break the File or Folder name down to 6 characters, plus ~n (n=occurrence)
If we had a C:\Program Files (x86) path then, like we do today, it would be the 2nd path who's first 6 characters matched, and both exceeded 8 characters.
C:\Program Files becomes -> C:\Progra~1\
C:\Program Files (x86) becomes -> C:\Progra~2\
C:\MyLongFilename.txt becomes -> C:\MyLong~1\
Whenever I am having trouble accessing the full length file system through software that is unable to send escape sequences or quotations, some other kind of limitation, I have to resort to using the 8.3 short filename to access certain paths. On some Windows boxes, quotes won't even work and it will be some LONG process to enable them on the host machine. This makes for a good workaround when that happens.
Getting the short path (via sending to command prompt)
C:\ for %A in ("C:\Program Files (x86)\CMake\bin\cmake.exe") do #echo %~sA
will produce C:\Progra~1 for you to use
Or, get the short path by sending the path as an argument to a batch file.
::getShortPath.bat
#ECHO OFF
echo %~s1
USE:-> getShortPath.bat "C:\Program Files (x86)\CMake\bin\cmake.exe"
To wrap this up, here are three examples of what could be happening in the background behind CMake when a windows path is not resolving.
Not using quotations around the path
Using quotes works. But sometimes you can lose your quotes if the stdio >> runs through more than one process. In which case you'll need to send them in as escape sequences "\"C:/Program Files (x86)/CMake/bin/cmake.exe\""
4. Paths and Command Line Arguments need to be separate variable or instances from each other.
When sending arguments from CMake, you DO want them to be separate variables from the path variable. Set(CMAKE_EXE "C:\Program Files (x86)\CMake\bin\cmake.exe --version") will not work. Only paths and arguments with spaces in them need to be wrapped in quotes.
set(CMAKE_EXE "C:\Program Files (x86)\CMake\bin\cmake.exe" --version --trace "C:\My Soure Dir")
Putting it all together
If anyone is having problems with Windows/CMake paths like I was in the past, Study this code thoroughly until you completely understand it. All of the quotation placements. When you understanding what's quoted and what's not, and why, it should help a lot in the long run.
set(CMAKE_EXE "C:\Program Files (x86)\CMake\cmake.exe" CACHE INTERNAL "") <- make it a global variable.
set(ARGUMENTS --version --trace)
set(MyStringWithQuotesIncluded "\"This String wants it's quotes included\"")
set(MyCMakeLists "C:\MyApp\ProjectDirectory")
set(BuildHere "C:\MyBuilds\MyOSProject\bin")
set(FULL_COMMAND "${CMAKE_EXE}" ${ARGUMENTS} -DSTRING_VARIABLE="${MyStringWithQuotesIncluded}" -S "${MyCMakeLists}" -B "${BuildHere}")
execute_command(COMMAND cmd /c ${FULL_COMMAND} WORKING_DIRECTORY "${BuildHere}")
I had loads of issues working with windows paths through layers of CMake when I first started out. I hope this can help someone avoid all of that in the future.
Step 0: Install CMAKE
Make sure you have CMAKE installed on Windows:
https://cmake.org/download/
The installer will ask you if you want it to automatically set the PATH variable for you.
set the path to C:\Program Files\CMake\bin

Batch/CMD: Adding files to Startup list

How can a batch file lists itself in the startup list of Windows???
It doesn't matter if it goes from the registry or not.
IF with the registry, please give also the command to DELETE the registry entry.
This should work under all versions from ME to 7 please.
Otherwise just XP/Vista/7.
Thanks.
Not sure i understand you, but if what you want is an easy way to execute a command/batch on startup, why not just put it in the All Users\Startup folder?
To do so programatically would just mean copying a file to that directory.
For example, in Windows Vista, the full path of that directory is:
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
(you can use replace the beginning of the line with %ProgramData% or %AllUsers%\ProgramData to make it more global - such as when Windows is installed on D:).
I do not use windows7 (might get a check at the beta shortly), but I think the correct place will always be better taken from the registry, because of the Windows versions being localized. My own version of C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup here looks more like "C:\Documents and Settings\All Users\Menu Démarrer\Programmes\Démarrage" (from XP, of course)
-10 for programmers using hard-coded directory names (yes, some installers will create english/different language directories at installation).
-1 for Microsoft localising directory names...
Anyhow here is a snipet for this, valid for XP at least:
commonstartup.cmd
#echo off
for /F "tokens=3 delims= " %%k in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Common Startup"^| findstr /i /c:"Common Startup"') do set StartUp=%%k
echo StartUp="%StartUp%"
___Notes_____
1: Because reg.exe from Windows2000 and XP have different command arguments, maybe the W7 one has changed too so test it before set and forget.
2: To get a list of all the system directories, issue the command: reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" and read the lines. You might want to change the "Common Startup" for something else, if things are so different with W7.
3: There is also a personal/user list within HKEY_CURRENT_USER if you want this to be usable by some users only.
xcopy C:\Users\NAME\Desktop\Batch.bat C:\ProgramData\Microsoft\Windows\"Start Menu"\Programs\StartUp /O /X /E /H /K
is the correct command for windows 10. simply change the the second path to your version, and remember whenever there is a space, place a " before the word before the space, and after the word after it.
however, it MUST be opened in administrator, so after some research, i found that a batch file could be used to start a different batch file and run it in administrative mode:
runas /user:administrator C:\data\mybatchfile.bat
that should work!