diff --git a/Modules/CMakeCCompilerId.c b/Modules/CMakeCCompilerId.c index a29c6a06c..8a8753aab 100644 --- a/Modules/CMakeCCompilerId.c +++ b/Modules/CMakeCCompilerId.c @@ -41,14 +41,9 @@ /* sdcc, the small devices C compiler for embedded systems, http://sdcc.sourceforge.net - Beside this id not supported yet by CMake - Unfortunately this doesn't work because SDCC (and other embedded compilers - too) produce not binary files, but e.g. Intel hex files by default. - This also means it has a different suffix (.ihx) so the file isn't even - found. */ -/* + Beside this id not supported yet by CMake. */ #elif defined(SDCC) -# define COMPILER_ID "SDCC" */ +# define COMPILER_ID "SDCC" #elif defined(_COMPILER_VERSION) # define COMPILER_ID "MIPSpro" diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index a0c0164b5..f0fce8fe7 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -66,56 +66,52 @@ MACRO(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) "${CMAKE_${lang}_COMPILER_ID_SRC}\" succeeded with the following output:\n" "${CMAKE_${lang}_COMPILER_ID_OUTPUT}\n\n") - # Find the executable produced by the compiler. - SET(CMAKE_${lang}_COMPILER_ID_EXE) - GET_FILENAME_COMPONENT(CMAKE_${lang}_COMPILER_ID_SRC_BASE ${CMAKE_${lang}_COMPILER_ID_SRC} NAME_WE) - FOREACH(name a.out a.exe ${CMAKE_${lang}_COMPILER_ID_SRC_BASE}.exe) - IF(EXISTS ${CMAKE_${lang}_COMPILER_ID_DIR}/${name}) - SET(CMAKE_${lang}_COMPILER_ID_EXE ${CMAKE_${lang}_COMPILER_ID_DIR}/${name}) - ENDIF(EXISTS ${CMAKE_${lang}_COMPILER_ID_DIR}/${name}) - ENDFOREACH(name) - - # Check if the executable was found. - IF(CMAKE_${lang}_COMPILER_ID_EXE) - # The executable was found. + # Find the executable produced by the compiler, try all files in the binary dir + SET(CMAKE_${lang}_COMPILER_ID) + FILE(GLOB COMPILER_${lang}_PRODUCED_FILES ${CMAKE_${lang}_COMPILER_ID_DIR}/*) + FOREACH(CMAKE_${lang}_COMPILER_ID_EXE ${COMPILER_${lang}_PRODUCED_FILES}) FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Compilation of the ${lang} compiler identification source \"" "${CMAKE_${lang}_COMPILER_ID_SRC}\" produced \"" "${CMAKE_${lang}_COMPILER_ID_EXE}\"\n\n") + # only check if we don't have it yet + IF(NOT CMAKE_${lang}_COMPILER_ID) + # Read the compiler identification string from the executable file. + FILE(STRINGS ${CMAKE_${lang}_COMPILER_ID_EXE} + CMAKE_${lang}_COMPILER_ID_STRINGS LIMIT_COUNT 2 REGEX "INFO:") + FOREACH(info ${CMAKE_${lang}_COMPILER_ID_STRINGS}) + IF("${info}" MATCHES ".*INFO:compiler\\[([^]]*)\\].*") + STRING(REGEX REPLACE ".*INFO:compiler\\[([^]]*)\\].*" "\\1" + CMAKE_${lang}_COMPILER_ID "${info}") + ENDIF("${info}" MATCHES ".*INFO:compiler\\[([^]]*)\\].*") + IF("${info}" MATCHES ".*INFO:platform\\[([^]]*)\\].*") + STRING(REGEX REPLACE ".*INFO:platform\\[([^]]*)\\].*" "\\1" + CMAKE_${lang}_PLATFORM_ID "${info}") + ENDIF("${info}" MATCHES ".*INFO:platform\\[([^]]*)\\].*") + ENDFOREACH(info) - # Read the compiler identification string from the executable file. - FILE(STRINGS ${CMAKE_${lang}_COMPILER_ID_EXE} - CMAKE_${lang}_COMPILER_ID_STRINGS LIMIT_COUNT 2 REGEX "INFO:") - FOREACH(info ${CMAKE_${lang}_COMPILER_ID_STRINGS}) - IF("${info}" MATCHES ".*INFO:compiler\\[([^]]*)\\].*") - STRING(REGEX REPLACE ".*INFO:compiler\\[([^]]*)\\].*" "\\1" - CMAKE_${lang}_COMPILER_ID "${info}") - ENDIF("${info}" MATCHES ".*INFO:compiler\\[([^]]*)\\].*") - IF("${info}" MATCHES ".*INFO:platform\\[([^]]*)\\].*") - STRING(REGEX REPLACE ".*INFO:platform\\[([^]]*)\\].*" "\\1" - CMAKE_${lang}_PLATFORM_ID "${info}") - ENDIF("${info}" MATCHES ".*INFO:platform\\[([^]]*)\\].*") - ENDFOREACH(info) + # Check the compiler identification string. + IF(CMAKE_${lang}_COMPILER_ID) + # The compiler identification was found. + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "The ${lang} compiler identification is ${CMAKE_${lang}_COMPILER_ID}, found in \"" + "${CMAKE_${lang}_COMPILER_ID_EXE}\"\n\n") + ELSE(CMAKE_${lang}_COMPILER_ID) + # The compiler identification could not be found. + FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "The ${lang} compiler identification could not be found in \"" + "${CMAKE_${lang}_COMPILER_ID_EXE}\"\n\n") + ENDIF(CMAKE_${lang}_COMPILER_ID) + ENDIF(NOT CMAKE_${lang}_COMPILER_ID) + ENDFOREACH(CMAKE_${lang}_COMPILER_ID_EXE) - # Check the compiler identification string. - IF(CMAKE_${lang}_COMPILER_ID) - # The compiler identification was found. - FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "The ${lang} compiler identification is ${CMAKE_${lang}_COMPILER_ID}\n\n") - ELSE(CMAKE_${lang}_COMPILER_ID) - # The compiler identification could not be found. - FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "The ${lang} compiler identification could not be found in \"" - "${CMAKE_${lang}_COMPILER_ID_EXE}\"\n\n") - ENDIF(CMAKE_${lang}_COMPILER_ID) - ELSE(CMAKE_${lang}_COMPILER_ID_EXE) - # The executable was not found. + IF(NOT COMPILER_${lang}_PRODUCED_FILES) + # No executable was found. FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Compilation of the ${lang} compiler identification source \"" "${CMAKE_${lang}_COMPILER_ID_SRC}\" did not produce an executable in " - "${CMAKE_${lang}_COMPILER_ID_DIR} " - "with a name known to CMake.\n\n") - ENDIF(CMAKE_${lang}_COMPILER_ID_EXE) + "${CMAKE_${lang}_COMPILER_ID_DIR} .\n\n") + ENDIF(NOT COMPILER_${lang}_PRODUCED_FILES) IF(CMAKE_${lang}_COMPILER_ID) MESSAGE(STATUS "The ${lang} compiler identification is " diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index c575043b5..01e2595a0 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -1,5 +1,5 @@ # just install the modules -# new file added, force rerunning cmake # +# new file added, force rerunning cmake SUBDIRS(Platform) INSTALL_FILES(${CMAKE_DATA_DIR}/Modules .*\\.cmake$) diff --git a/Source/cmBootstrapCommands.cxx b/Source/cmBootstrapCommands.cxx index 19be6dfd5..f129eb08a 100644 --- a/Source/cmBootstrapCommands.cxx +++ b/Source/cmBootstrapCommands.cxx @@ -30,6 +30,7 @@ #include "cmBuildCommand.cxx" #include "cmCMakeMinimumRequired.cxx" #include "cmConfigureFileCommand.cxx" +#include "cmCoreTryCompile.cxx" #include "cmCreateTestSourceList.cxx" #include "cmElseCommand.cxx" #include "cmEnableTestingCommand.cxx" @@ -47,6 +48,7 @@ #include "cmGetCMakePropertyCommand.cxx" #include "cmGetFilenameComponentCommand.cxx" #include "cmGetSourceFilePropertyCommand.cxx" +#include "cmHexFileConverter.cxx" #include "cmIfCommand.cxx" #include "cmIncludeCommand.cxx" #include "cmIncludeDirectoryCommand.cxx" @@ -66,7 +68,6 @@ #include "cmStringCommand.cxx" #include "cmSubdirCommand.cxx" #include "cmTargetLinkLibrariesCommand.cxx" -#include "cmCoreTryCompile.cxx" #include "cmTryCompileCommand.cxx" #include "cmTryRunCommand.cxx" diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index ed3ac2c5e..5b3390d72 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -15,6 +15,8 @@ =========================================================================*/ #include "cmFileCommand.h" +#include "cmake.h" +#include "cmHexFileConverter.h" #include #include @@ -414,6 +416,14 @@ bool cmFileCommand::HandleStringsCommand(std::vector const& args) return false; } } + + std::string binaryFileName = this->Makefile->GetCurrentOutputDirectory(); + binaryFileName += cmake::GetCMakeFilesDirectory(); + binaryFileName += "/FileCommandStringsBinaryFile"; + if (cmHexFileConverter::TryConvert(fileName.c_str(), binaryFileName.c_str())) + { + fileName = binaryFileName; + } // Open the specified file. #if defined(_WIN32) || defined(__CYGWIN__) diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index f758151d6..5868725f7 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -93,9 +93,10 @@ public: "want to generate input files to CMake.\n" "READ will read the content of a file and store it into the " "variable.\n" - "STRINGS will parse a list of ASCII strings from a file and store it " - "in a variable. Binary data in the file are ignored. Carriage return " - "(CR) characters are ignored. " + "STRINGS will parse a list of ASCII strings from a binary file and " + "store it in a variable. Binary data in the file are ignored. Carriage " + "return (CR) characters are ignored. It works also for Intel Hex and " + "Motorola S-record files.\n " "LIMIT_COUNT sets the maximum number of strings to return. " "LIMIT_INPUT sets the maximum number of bytes to read from " "the input file. " diff --git a/Source/cmHexFileConverter.cxx b/Source/cmHexFileConverter.cxx new file mode 100644 index 000000000..f8d369f1d --- /dev/null +++ b/Source/cmHexFileConverter.cxx @@ -0,0 +1,266 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#include "cmHexFileConverter.h" + +#include +#include + +#define INTEL_HEX_MIN_LINE_LENGTH (1+8 +2) +#define INTEL_HEX_MAX_LINE_LENGTH (1+8+(256*2)+2) +#define MOTOROLA_SREC_MIN_LINE_LENGTH (2+2+4 +2) +#define MOTOROLA_SREC_MAX_LINE_LENGTH (2+2+8+(256*2)+2) + +// might go to SystemTools ? +static bool cm_IsHexChar(char c) +{ + return (((c >= '0') && (c <= '9')) + || ((c >= 'a') && (c <= 'f')) + || ((c >= 'A') && (c <= 'F'))); +} + +static unsigned int ChompStrlen(const char* line) +{ + if (line == 0) + { + return 0; + } + unsigned int length = strlen(line); + if ((line[length-1] == '\n') || (line[length-1] == '\r')) + { + length--; + } + if ((line[length-1] == '\n') || (line[length-1] == '\r')) + { + length--; + } + return length; +} + +static bool OutputBin(FILE* file, const char * buf, + unsigned int startIndex, unsigned int stopIndex) +{ + bool success = true; + char hexNumber[3]; + hexNumber[2] = '\0'; + char outBuf[256]; + int outBufCount = 0; + for (unsigned int i = startIndex; i < stopIndex; i += 2) + { + hexNumber[0] = buf[i]; + hexNumber[1] = buf[i+1]; + unsigned int convertedByte = 0; + if (sscanf(hexNumber, "%x", &convertedByte) != 1) + { + success = false; + break; + } + outBuf[outBufCount] = convertedByte & 0xff; + outBufCount++; + } + if (success) + { + success = (fwrite(outBuf, 1, outBufCount, file)==outBufCount); + } + return success; +} + +// see http://www.die.net/doc/linux/man/man5/srec.5.html +static bool ConvertMotorolaSrecLine(const char* buf, FILE* outFile) +{ + unsigned int slen = ChompStrlen(buf); + if ((slen < MOTOROLA_SREC_MIN_LINE_LENGTH) + || (slen > MOTOROLA_SREC_MAX_LINE_LENGTH)) + { + return false; + } + + // line length must be even + if (slen % 2 == 1) + { + return false; + } + + if (buf[0] != 'S') + { + return false; + } + + unsigned int dataStart = 0; + // ignore extra address records + if ((buf[1] == '5') || (buf[1] == '7') || (buf[1] == '8') || (buf[1] == '9')) + { + return true; + } + else if (buf[1] == '1') + { + dataStart = 8; + } + else if (buf[1] == '2') + { + dataStart = 10; + } + else if (buf[1] == '3') + { + dataStart = 12; + } + else // unknown record type + { + return false; + } + + // ignore the last two bytes (checksum) + return OutputBin(outFile, buf, dataStart, slen - 2); +} + +// see http://en.wikipedia.org/wiki/Intel_hex +static bool ConvertIntelHexLine(const char* buf, FILE* outFile) +{ + unsigned int slen = ChompStrlen(buf); + if ((slen < INTEL_HEX_MIN_LINE_LENGTH) + || (slen > INTEL_HEX_MAX_LINE_LENGTH)) + { + return false; + } + + // line length must be odd + if (slen % 2 == 0) + { + return false; + } + + if ((buf[0] != ':') || (buf[7] != '0')) + { + return false; + } + + unsigned int dataStart = 0; + if ((buf[8] == '0') || (buf[8] == '1')) + { + dataStart = 9; + } + // ignore extra address records + else if ((buf[8] == '2') || (buf[8] == '3') || (buf[8] == '4') || (buf[8] == '5')) + { + return true; + } + else // unknown record type + { + return false; + } + +// ignore the last two bytes (checksum) + return OutputBin(outFile, buf, dataStart, slen - 2); +} + +cmHexFileConverter::FileType cmHexFileConverter::DetermineFileType( + const char* inFileName) +{ + char buf[1024]; + FILE* inFile = fopen(inFileName, "rb"); + if (inFile == 0) + { + return Binary; + } + + fgets(buf, 1024, inFile); + fclose(inFile); + FileType type = Binary; + unsigned int minLineLength = 0; + unsigned int maxLineLength = 0; + if (buf[0] == ':') // might be an intel hex file + { + type = IntelHex; + minLineLength = INTEL_HEX_MIN_LINE_LENGTH; + maxLineLength = INTEL_HEX_MAX_LINE_LENGTH; + } + else if (buf[0] == 'S') // might be a motorola srec file + { + type = MotorolaSrec; + minLineLength = MOTOROLA_SREC_MIN_LINE_LENGTH; + maxLineLength = MOTOROLA_SREC_MAX_LINE_LENGTH; + } + else + { + return Binary; + } + + int slen = ChompStrlen(buf); + if ((slen < minLineLength) || (slen > maxLineLength)) + { + return Binary; + } + + for (unsigned int i = 1; i < slen; i++) + { + if (!cm_IsHexChar(buf[i])) + { + return Binary; + } + } + return type; +} + +bool cmHexFileConverter::TryConvert(const char* inFileName, + const char* outFileName) +{ + FileType type = DetermineFileType(inFileName); + if (type == Binary) + { + return false; + } + + // try to open the file + FILE* inFile = fopen(inFileName, "rb"); + FILE* outFile = fopen(outFileName, "wb"); + if ((inFile == 0) || (outFile == 0)) + { + if (inFile != 0) + { + fclose(inFile); + } + if (outFile != 0) + { + fclose(outFile); + } + return false; + } + + // convert them line by line + bool success = false; + char buf[1024]; + while (fgets(buf, 1024, inFile) != 0) + { + if (type == MotorolaSrec) + { + success = ConvertMotorolaSrecLine(buf, outFile); + } + else if (type == IntelHex) + { + success = ConvertIntelHexLine(buf, outFile); + } + if (success == false) + { + break; + } + } + + // close them again + fclose(inFile); + fclose(outFile); + return success; +} + diff --git a/Source/cmHexFileConverter.h b/Source/cmHexFileConverter.h new file mode 100644 index 000000000..8b6dc7226 --- /dev/null +++ b/Source/cmHexFileConverter.h @@ -0,0 +1,33 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef cmHexFileConverter_h +#define cmHexFileConverter_h + +/** \class cmHexFileConverter + * \brief Can detects Intel Hex and Motorola S-record files and convert them + * to binary files. + * + */ +class cmHexFileConverter +{ +public: + enum FileType {Binary, IntelHex, MotorolaSrec}; + static FileType DetermineFileType(const char* inFileName); + static bool TryConvert(const char* inFileName, const char* outFileName); +}; + +#endif diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt index c2bb53f83..36d15ce0b 100644 --- a/Tests/StringFileTest/CMakeLists.txt +++ b/Tests/StringFileTest/CMakeLists.txt @@ -16,6 +16,28 @@ ELSE("${infile_strings}" STREQUAL "${infile_strings_goal}") "FILE(STRINGS) incorrectly read [${infile_strings}]") ENDIF("${infile_strings}" STREQUAL "${infile_strings_goal}") + +# test that FILE(STRINGS) also work with Intel hex and Motorola S-record files +# this file has been created with "sdcc main.c" +FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/main.ihx" infile_strings REGEX INFO) +SET(infile_strings_goal "INFO:compiler\\[SDCC-HEX\\]") +IF("${infile_strings}" MATCHES "${infile_strings_goal}") + MESSAGE("FILE(STRINGS) correctly read from hex file [${infile_strings}]") +ELSE("${infile_strings}" MATCHES "${infile_strings_goal}") + MESSAGE(SEND_ERROR + "FILE(STRINGS) incorrectly read from hex file [${infile_strings}]") +ENDIF("${infile_strings}" MATCHES "${infile_strings_goal}") + +# this file has been created with "sdcc main.c --out-fmt-s19" +FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/main.srec" infile_strings REGEX INFO) +SET(infile_strings_goal "INFO:compiler\\[SDCC-SREC\\]") +IF("${infile_strings}" MATCHES "${infile_strings_goal}") + MESSAGE("FILE(STRINGS) correctly read from srec file [${infile_strings}]") +ELSE("${infile_strings}" MATCHES "${infile_strings_goal}") + MESSAGE(SEND_ERROR + "FILE(STRINGS) incorrectly read from srec file [${infile_strings}]") +ENDIF("${infile_strings}" MATCHES "${infile_strings_goal}") + # String test STRING(REGEX MATCH "[cC][mM][aA][kK][eE]" rmvar "CMake is great") STRING(REGEX MATCHALL "[cC][mM][aA][kK][eE]" rmallvar "CMake is better than cmake or CMake") diff --git a/Tests/StringFileTest/main.ihx b/Tests/StringFileTest/main.ihx new file mode 100644 index 000000000..c1d1dd2b3 --- /dev/null +++ b/Tests/StringFileTest/main.ihx @@ -0,0 +1,21 @@ +:03000000020003F8 +:03005C0002005F40 +:05005F0012006480FEA8 +:010064002279 +:0E006900494E464F3A636F6D70696C65725B6D +:0A007700534443432D4845585D00F3 +:06003200E478FFF6D8FDA2 +:080010007900E94400601B7A4D +:0500180000900081785A +:03001D000075A0CB +:0A00200000E493F2A308B800020503 +:08002A00A0D9F4DAF275A0FF81 +:080038007800E84400600A7939 +:030040000075A0A8 +:0600430000E4F309D8FC03 +:080049007800E84400600C7926 +:0B00510000900000E4F0A3D8FCD9FAF6 +:03000300758107FD +:0A000600120065E582600302005F4E +:04006500758200227E +:00000001FF diff --git a/Tests/StringFileTest/main.srec b/Tests/StringFileTest/main.srec new file mode 100644 index 000000000..bd47c29cc --- /dev/null +++ b/Tests/StringFileTest/main.srec @@ -0,0 +1,21 @@ +S1060000020003F4 +S106005C02005F3C +S108005F12006480FEA4 +S10400642275 +S1110069494E464F3A636F6D70696C65725B69 +S10E0077534443432D535245435D00A6 +S1090032E478FFF6D8FD9E +S10B00107900E94400601B7A49 +S1080018009000827855 +S106001D0075A0C7 +S10D002000E493F2A308B8000205FF +S10B002AA0D9F4DAF275A0FF7D +S10B00387800E84400600A7935 +S10600400075A0A4 +S109004300E4F309D8FCFF +S10B00497800E84400600C7922 +S10E005100900000E4F0A3D8FCD9FAF2 +S1060003758107F9 +S10D0006120065E582600302005F4A +S1070065758200227A +S9030000FC