There are two types of generators: with one configuration and with several configurations.
Single configurations
Make-like generators: Makefiles Unix , NMake Makefiles , MinGW Makefiles , ...
You specify the type of configuration in the generation step:
cmake -H. -B_builds/Debug -DCMAKE_BUILD_TYPE=Debug "-GUnix Makefiles"
In this case, the build phase is always Debug:
> cmake --build _builds/Debug /usr/bin/c++ -g ... > cmake --build _builds/Debug --config Debug
Multi-configuration
IDE Generators: Visual Studio , Xcode
CMAKE_BUILD_TYPE in the generation step is ignored:
> cmake -H. -B_builds -DCMAKE_BUILD_TYPE=Debug "-GVisual Studio 12 2013 Win64"
and
> cmake -H. -B_builds -DCMAKE_BUILD_TYPE=Release "-GVisual Studio 12 2013 Win64"
will have the same effect:

This is because all configurations are internal (i.e. _builds/msvc-opaque/Release and _builds/msvc-opaque/Debug or something, it doesn't matter). You can use the --config options to switch:
> cmake --build _builds --config Release cl /O2 ... > cmake --build _builds --config Debug cl /Od ...
The control (?)
Yes you can. Just define CMAKE_CONFIGURATION_TYPES :
# Somewhere in CMakeLists.txt message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}")
Default output:
-- Detecting CXX compiler ABI info - done Generated with config types: Debug;Release;MinSizeRel;RelWithDebInfo -- Configuring done
Rewrite it:
> cmake -H. -B_builds -DCMAKE_CONFIGURATION_TYPES="Debug;Release" "-GVisual Studio 12 2013 Win64" -- Detecting CXX compiler ABI info - done Generated with config types: Debug;Release -- Configuring done

You can even define your own configuration type:
> cmake -H. -B_builds -DCMAKE_CONFIGURATION_TYPES="Debug;MyRelease" -DCMAKE_CXX_FLAGS_MYRELEASE="/My-Rel-flag" -DCMAKE_EXE_LINKER_FLAGS_MYRELEASE="/My-Linker-flags" "-GVisual Studio 12 2013 Win64"

And build:
cmake --build _builds --config MyRelease
Dirty (?)
Not at all, if you know the trick :) Here's how to build / test the configuration in the script / on the CI server, in the documentation assembly instructions, etc.:
> CONFIG=Debug > cmake -H. -B_builds "-DCMAKE_BUILD_TYPE=${CONFIG}" # Set Debug to Makefile, ignored by IDE > cmake --build _builds --config "${CONFIG}" # Build Debug in IDE, ignored by Makefile > (cd _builds && ctest -VV -C "${CONFIG}") # Test Debug in IDE, ignored by Makefile
Bad picture
if(CMAKE_BUILD_TYPE STREQUAL Debug)
Good
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} --my-debug-flags")
Works good.
target_compile_definitions(MyTarget PUBLIC "$<$<CONFIG:Debug>:MYDEBUG_MACRO>")
Thank! :) You save a day for one programmer.
Works with Makefile for me, I'm happy ...
Some quotes from a good guy ’s good book that you probably know (emphasis mine):
Why should you worry? People who program on different systems or use different compilers are very concerned, because if they do not, they have to spend time searching and fixing obscure errors. People who claim that they do not care about mobility usually do this because they use only one system and feel that they can afford the attitude that language is what my compiler implements. This is a narrow and shortsighted view. If your program is successful, it will most likely be ported, so someone will have to look for and fix problems associated with the implementation-dependent functions. In addition, programs often need to be compiled with other compilers for the same system, and even in a future release, your favorite compiler may do some things differently from the current one. It is much easier to learn and limit the effect of implementation dependencies when a program is written than to try to unravel the mess afterwards.