Browse Source

ENH: Rename SET_PROPERITES command to SET_PROPERTY and give it a more powerful signature.

Brad King 18 years ago
parent
commit
b8357db11d

+ 1 - 1
Modules/CMakeGenericSystem.cmake

@@ -21,7 +21,7 @@ SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
 SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so" ".a")
 
 # basically all general purpose OSs support shared libs
-SET_PROPERTIES(GLOBAL PROPERTIES TARGET_SUPPORTS_SHARED_LIBS TRUE)
+SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)
 
 SET (CMAKE_SKIP_RPATH "NO" CACHE BOOL
      "If set, runtime paths are not added when using shared libraries.")

+ 3 - 3
Modules/FeatureSummary.cmake

@@ -21,12 +21,12 @@
 MACRO(SET_FEATURE_INFO _name _desc)
   SET(_url "${ARGV2}")
   SET(_comment "${ARGV3}")
-  SET_PROPERTIES(GLOBAL PROPERTIES ${_name}_DESCRIPTION "${_desc}" ) 
+  SET_PROPERTY(GLOBAL PROPERTY ${_name}_DESCRIPTION "${_desc}" )
   IF(_url MATCHES ".+")
-    SET_PROPERTIES(GLOBAL PROPERTIES ${_name}_URL "${_url}" ) 
+    SET_PROPERTY(GLOBAL PROPERTY ${_name}_URL "${_url}" )
   ENDIF(_url MATCHES ".+")
   IF(_comment MATCHES ".+")
-    SET_PROPERTIES(GLOBAL PROPERTIES ${_name}_COMMENT "${_comment}" ) 
+    SET_PROPERTY(GLOBAL PROPERTY ${_name}_COMMENT "${_comment}" )
   ENDIF(_comment MATCHES ".+")
 ENDMACRO(SET_FEATURE_INFO)
 

+ 1 - 1
Modules/Platform/BlueGeneL.cmake

@@ -1,5 +1,5 @@
 #the compute nodes on BlueGene/L don't support shared libs 
-SET_PROPERTIES(GLOBAL PROPERTIES TARGET_SUPPORTS_SHARED_LIBS FALSE)
+SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
 
 SET(CMAKE_SHARED_LIBRARY_C_FLAGS "")            # -pic 
 SET(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "")       # -shared

+ 1 - 1
Modules/Platform/Catamount.cmake

@@ -1,5 +1,5 @@
 #Catamount, which runs on the compute nodes of Cray machines, e.g. RedStorm, doesn't support shared libs 
-SET_PROPERTIES(GLOBAL PROPERTIES TARGET_SUPPORTS_SHARED_LIBS FALSE)
+SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
 
 SET(CMAKE_SHARED_LIBRARY_C_FLAGS "")            # -pic 
 SET(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "")       # -shared

+ 1 - 2
Modules/Platform/Generic.cmake

@@ -8,5 +8,4 @@
 # and/or ${CMAKE_SYSTEM_NAME}-<compiler_basename>-${CMAKE_SYSTEM_PROCESSOR}.cmake
 
 # (embedded) targets without operating system usually don't support shared libraries
-SET_PROPERTIES(GLOBAL PROPERTIES TARGET_SUPPORTS_SHARED_LIBS FALSE)
-
+SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)

+ 1 - 1
Modules/Platform/Linux.cmake

@@ -52,5 +52,5 @@ INCLUDE(Platform/UnixPaths)
 # Debian has lib64 paths only for compatibility so they should not be
 # searched.
 IF(EXISTS "/etc/debian_version")
-  SET_PROPERTIES(GLOBAL PROPERTIES FIND_LIBRARY_USE_LIB64_PATHS FALSE)
+  SET_PROPERTY(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS FALSE)
 ENDIF(EXISTS "/etc/debian_version")

+ 1 - 1
Modules/Platform/UnixPaths.cmake

@@ -54,4 +54,4 @@ LIST(APPEND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES
   )
 
 # Enable use of lib64 search path variants by default.
-SET_PROPERTIES(GLOBAL PROPERTIES FIND_LIBRARY_USE_LIB64_PATHS TRUE)
+SET_PROPERTY(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE)

+ 1 - 1
Modules/Platform/eCos.cmake

@@ -47,7 +47,7 @@ SET(CMAKE_CXX_LINK_EXECUTABLE  "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLA
 SET(CMAKE_C_LINK_EXECUTABLE    "<CMAKE_C_COMPILER>   <FLAGS> <CMAKE_C_LINK_FLAGS>   <LINK_FLAGS> <OBJECTS> -o <TARGET> -nostdlib -nostartfiles -L${ECOS_LIBTARGET_DIRECTORY} -Ttarget.ld  <LINK_LIBRARIES>")
 
 # eCos doesn't support shared libs
-SET_PROPERTIES(GLOBAL PROPERTIES TARGET_SUPPORTS_SHARED_LIBS FALSE)
+SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
 
 SET(CMAKE_CXX_LINK_SHARED_LIBRARY )
 SET(CMAKE_CXX_LINK_MODULE_LIBRARY )

+ 2 - 2
Source/cmBootstrapCommands.cxx

@@ -72,7 +72,7 @@
 #include "cmProjectCommand.cxx"
 #include "cmRaiseScopeCommand.cxx"
 #include "cmSetCommand.cxx"
-#include "cmSetPropertiesCommand.cxx"
+#include "cmSetPropertyCommand.cxx"
 #include "cmSetSourceFilesPropertiesCommand.cxx"
 #include "cmSetTargetPropertiesCommand.cxx"
 #include "cmSetTestsPropertiesCommand.cxx"
@@ -132,7 +132,7 @@ void GetBootstrapCommands(std::list<cmCommand*>& commands)
   commands.push_back(new cmProjectCommand);
   commands.push_back(new cmRaiseScopeCommand);
   commands.push_back(new cmSetCommand);
-  commands.push_back(new cmSetPropertiesCommand);
+  commands.push_back(new cmSetPropertyCommand);
   commands.push_back(new cmSetSourceFilesPropertiesCommand);
   commands.push_back(new cmSetTargetPropertiesCommand);
   commands.push_back(new cmSetTestsPropertiesCommand);

+ 0 - 210
Source/cmSetPropertiesCommand.cxx

@@ -1,210 +0,0 @@
-/*=========================================================================
-
-  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 "cmSetPropertiesCommand.h"
-#include "cmSetTargetPropertiesCommand.h"
-#include "cmSetTestsPropertiesCommand.h"
-#include "cmSetSourceFilesPropertiesCommand.h"
-
-// cmSetPropertiesCommand
-bool cmSetPropertiesCommand::InitialPass(
-  std::vector<std::string> const& args)
-{
-  if(args.size() < 2 )
-    {
-    this->SetError("called with incorrect number of arguments");
-    return false;
-    }
-
-  // first collect up the list of files
-  std::vector<std::string> propertyPairs;
-  bool doingFiles = true;
-  int numFiles = 0;
-  std::vector<std::string>::const_iterator j;
-  for(j= args.begin(); j != args.end();++j)
-    {
-    if(*j == "PROPERTIES")
-      {
-      doingFiles = false;
-      // now loop through the rest of the arguments, new style
-      ++j;
-      while (j != args.end())
-        {
-        propertyPairs.push_back(*j);
-        ++j;
-        if(j == args.end())
-          {
-          this->SetError("called with incorrect number of arguments.");
-          return false;
-          }
-        propertyPairs.push_back(*j);
-        ++j;
-        }
-      // break out of the loop because j is already == end
-      break;
-      }
-    else if (doingFiles)
-      {
-      numFiles++;
-      }
-    else
-      {
-      this->SetError("called with illegal arguments, maybe missing "
-                     "a PROPERTIES specifier?");
-      return false;
-      }
-    }
-  if(propertyPairs.size() == 0)
-    {
-     this->SetError("called with illegal arguments, maybe missing "
-                    "a PROPERTIES specifier?");
-     return false;
-    }
-  
-  cmProperty::ScopeType scope;
-  const char *scopeName = 0;
-  if (args[0] == "GLOBAL" && numFiles == 1)
-    {
-    scope = cmProperty::GLOBAL;
-    }
-  else if (args[0] == "DIRECTORY" && numFiles >= 1)
-    {
-    scope = cmProperty::DIRECTORY;
-    if (numFiles == 2)
-      {
-      scopeName = args[1].c_str();
-      }
-    }
-  else if (args[0] == "TARGET" && numFiles == 2)
-    {
-    scope = cmProperty::TARGET;
-    scopeName = args[1].c_str();
-    }
-  else if (args[0] == "TEST" && numFiles == 2)
-    {
-    scope = cmProperty::TEST;
-    scopeName = args[1].c_str();
-    }
-  else if (args[0] == "SOURCE_FILE" && numFiles == 2)
-    {
-    scope = cmProperty::SOURCE_FILE;
-    scopeName = args[1].c_str();
-    }
-  else
-    {
-    this->SetError("called with illegal arguments.");
-    return false;
-    }
-
-  switch (scope) 
-    {
-    case cmProperty::TARGET:
-      {
-      bool ret = cmSetTargetPropertiesCommand::
-        SetOneTarget(scopeName,propertyPairs, this->Makefile);
-      if (!ret)
-        {
-        std::string message = "Can not find target to add properties to: ";
-        message += scopeName;
-        this->SetError(message.c_str());
-        return ret;
-        }
-      }
-      break;
-    case cmProperty::DIRECTORY:
-      {
-      // lookup the makefile from the directory name
-      cmLocalGenerator *lg = this->Makefile->GetLocalGenerator();
-      if (numFiles == 2)
-        {
-        std::string sd = scopeName;
-        // make sure the start dir is a full path
-        if (!cmSystemTools::FileIsFullPath(sd.c_str()))
-          {
-          sd = this->Makefile->GetStartDirectory();
-          sd += "/";
-          sd += scopeName;
-          }
-        
-        // The local generators are associated with collapsed paths.
-        sd = cmSystemTools::CollapseFullPath(sd.c_str());
-        
-        lg = this->Makefile->GetLocalGenerator()->GetGlobalGenerator()->
-          FindLocalGenerator(sd.c_str());
-        }
-      if (!lg)
-        {
-        this->SetError
-          ("DIRECTORY argument provided but requested directory not found. "
-           "This could be because the directory argument was invalid or, "
-           "it is valid but has not been processed yet.");
-        return false;
-        }
-      
-      for(j= propertyPairs.begin(); j != propertyPairs.end(); ++j)
-        {
-        const char *pn = j->c_str();
-        ++j;
-        lg->GetMakefile()->SetProperty(pn,j->c_str());
-        }
-      }
-      break;
-    case cmProperty::GLOBAL:
-      {
-      for(j= propertyPairs.begin(); j != propertyPairs.end(); ++j)
-        {
-        const char *pn = j->c_str();
-        ++j;
-        this->Makefile->GetCMakeInstance()->SetProperty(pn, j->c_str());
-        }
-      }
-      break;
-    case cmProperty::TEST:
-      {
-      std::string errors;
-      bool ret = cmSetTestsPropertiesCommand::
-        SetOneTest(scopeName,propertyPairs, this->Makefile, errors);
-      if (!ret)
-        {
-        this->SetError(errors.c_str());
-        return ret;
-        }
-      }
-      break;
-    case cmProperty::SOURCE_FILE:
-      {
-      std::string errors;
-      bool ret = cmSetSourceFilesPropertiesCommand::
-        RunCommand(this->Makefile,
-                   args.begin()+1, args.begin()+2,
-                   args.begin() + 2, args.end(),
-                   errors);
-      if (!ret)
-        {
-        this->SetError(errors.c_str());
-        return ret;
-        }
-      }
-      break;
-    case cmProperty::VARIABLE:
-    case cmProperty::CACHED_VARIABLE:
-      // not handled by SetProperty
-      break;
-    }
-
-  return true;
-}
-

+ 0 - 74
Source/cmSetPropertiesCommand.h

@@ -1,74 +0,0 @@
-/*=========================================================================
-
-  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 cmSetsPropertiesCommand_h
-#define cmSetsPropertiesCommand_h
-
-#include "cmCommand.h"
-
-class cmSetPropertiesCommand : public cmCommand
-{
-public:
-  virtual cmCommand* Clone() 
-    {
-      return new cmSetPropertiesCommand;
-    }
-
-  /**
-   * This is called when the command is first encountered in
-   * the input file.
-   */
-  virtual bool InitialPass(std::vector<std::string> const& args);
-
-  /**
-   * The name of the command as specified in CMakeList.txt.
-   */
-  virtual const char* GetName() { return "set_properties";}
-
-  /**
-   * Succinct documentation.
-   */
-  virtual const char* GetTerseDocumentation() 
-    {
-    return "Set properties used by CMake.";
-    }
-  
-  /**
-   * Longer documentation.
-   */
-  virtual const char* GetFullDocumentation()
-    {
-      return
-        "  set_properties(scope_value\n"
-        "                 PROPERTIES prop1 value1\n"
-        "                 prop2 value2 ...)\n"
-        "Set properties on something. The scope_value is either GLOBAL, "
-        "DIRECTORY dir_name, TARGET tgt_name, SOURCE_FILE src_name, "
-        "or TEST test_name."
-        ;
-    }
-
-  /**
-   * This determines if the command is invoked when in script mode.
-   */
-  virtual bool IsScriptable() { return true; }
-
-  cmTypeMacro(cmSetPropertiesCommand, cmCommand);
-};
-
-
-
-#endif

+ 408 - 0
Source/cmSetPropertyCommand.cxx

@@ -0,0 +1,408 @@
+/*=========================================================================
+
+  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 "cmSetPropertyCommand.h"
+#include "cmSetTargetPropertiesCommand.h"
+#include "cmSetTestsPropertiesCommand.h"
+#include "cmSetSourceFilesPropertiesCommand.h"
+
+//----------------------------------------------------------------------------
+cmSetPropertyCommand::cmSetPropertyCommand()
+{
+  this->AppendMode = false;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::InitialPass(std::vector<std::string> const& args)
+{
+  if(args.size() < 2 )
+    {
+    this->SetError("called with incorrect number of arguments");
+    return false;
+    }
+
+  // Get the scope on which to set the property.
+  std::vector<std::string>::const_iterator arg = args.begin();
+  cmProperty::ScopeType scope;
+  if(*arg == "GLOBAL")
+    {
+    scope = cmProperty::GLOBAL;
+    }
+  else if(*arg == "DIRECTORY")
+    {
+    scope = cmProperty::DIRECTORY;
+    }
+  else if(*arg == "TARGET")
+    {
+    scope = cmProperty::TARGET;
+    }
+  else if(*arg == "SOURCE")
+    {
+    scope = cmProperty::SOURCE_FILE;
+    }
+  else if(*arg == "TEST")
+    {
+    scope = cmProperty::TEST;
+    }
+  else
+    {
+    cmOStringStream e;
+    e << "given invalid scope " << *arg << ".  "
+      << "Valid scopes are GLOBAL, DIRECTORY, TARGET, SOURCE, TEST.";
+    this->SetError(e.str().c_str());
+    return false;
+    }
+
+  // Parse the rest of the arguments up to the values.
+  enum Doing { DoingNone, DoingNames, DoingProperty, DoingValues };
+  Doing doing = DoingNames;
+  const char* sep = "";
+  for(++arg; arg != args.end(); ++arg)
+    {
+    if(*arg == "PROPERTY")
+      {
+      doing = DoingProperty;
+      }
+    else if(*arg == "APPEND")
+      {
+      doing = DoingNone;
+      this->AppendMode = true;
+      }
+    else if(doing == DoingNames)
+      {
+      this->Names.insert(*arg);
+      }
+    else if(doing == DoingProperty)
+      {
+      this->PropertyName = *arg;
+      doing = DoingValues;
+      }
+    else if(doing == DoingValues)
+      {
+      this->PropertyValue += sep;
+      sep = ";";
+      this->PropertyValue += *arg;
+      }
+    else
+      {
+      cmOStringStream e;
+      e << "given invalid argument \"" << *arg << "\".";
+      this->SetError(e.str().c_str());
+      return false;
+      }
+    }
+
+  // Make sure a property name was found.
+  if(this->PropertyName.empty())
+    {
+    this->SetError("not given a PROPERTY <name> argument.");
+    return false;
+    }
+
+  // Dispatch property setting.
+  switch(scope)
+    {
+    case cmProperty::GLOBAL:      return this->HandleGlobalMode();
+    case cmProperty::DIRECTORY:   return this->HandleDirectoryMode();
+    case cmProperty::TARGET:      return this->HandleTargetMode();
+    case cmProperty::SOURCE_FILE: return this->HandleSourceMode();
+    case cmProperty::TEST:        return this->HandleTestMode();
+
+    case cmProperty::VARIABLE:
+    case cmProperty::CACHED_VARIABLE:
+      break; // should never happen
+    }
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::ConstructValue(std::string& value,
+                                          const char* old)
+{
+  if(this->AppendMode)
+    {
+    // This is an append.  Start with the original value.
+    if(old)
+      {
+      value = old;
+      }
+    }
+  else if(this->PropertyValue.empty())
+    {
+    // This is a set to no values.  Remove the property.
+    return false;
+    }
+
+  // Add the new value.
+  if(!this->PropertyValue.empty())
+    {
+    if(!value.empty())
+      {
+      value += ";";
+      }
+    value += this->PropertyValue;
+    }
+
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleGlobalMode()
+{
+  if(!this->Names.empty())
+    {
+    this->SetError("given names for GLOBAL scope.");
+    return false;
+    }
+
+  // Set or append the property.
+  cmake* cm = this->Makefile->GetCMakeInstance();
+  const char* name = this->PropertyName.c_str();
+  std::string value;
+  if(this->ConstructValue(value, cm->GetProperty(name)))
+    {
+    // Set the new property.
+    cm->SetProperty(name, value.c_str());
+    }
+  else
+    {
+    // Remove the property.
+    cm->SetProperty(name, 0);
+    }
+
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleDirectoryMode()
+{
+  if(this->Names.size() > 1)
+    {
+    this->SetError("allows at most one name for DIRECTORY scope.");
+    return false;
+    }
+
+  // Default to the current directory.
+  cmMakefile* mf = this->Makefile;
+
+  // Lookup the directory if given.
+  if(!this->Names.empty())
+    {
+    // Construct the directory name.  Interpret relative paths with
+    // respect to the current directory.
+    std::string dir = *this->Names.begin();
+    if(!cmSystemTools::FileIsFullPath(dir.c_str()))
+      {
+      dir = this->Makefile->GetCurrentDirectory();
+      dir += "/";
+      dir += *this->Names.begin();
+      }
+
+    // The local generators are associated with collapsed paths.
+    dir = cmSystemTools::CollapseFullPath(dir.c_str());
+
+    // Lookup the generator.
+    if(cmLocalGenerator* lg =
+       (this->Makefile->GetLocalGenerator()
+        ->GetGlobalGenerator()->FindLocalGenerator(dir.c_str())))
+      {
+      // Use the makefile for the directory found.
+      mf = lg->GetMakefile();
+      }
+    else
+      {
+      // Could not find the directory.
+      this->SetError
+        ("DIRECTORY scope provided but requested directory was not found. "
+         "This could be because the directory argument was invalid or, "
+         "it is valid but has not been processed yet.");
+      return false;
+      }
+    }
+
+  // Set or append the property.
+  const char* name = this->PropertyName.c_str();
+  std::string value;
+  if(this->ConstructValue(value, mf->GetProperty(name)))
+    {
+    // Set the new property.
+    mf->SetProperty(name, value.c_str());
+    }
+  else
+    {
+    // Remove the property.
+    mf->SetProperty(name, 0);
+    }
+
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleTargetMode()
+{
+  for(std::set<cmStdString>::const_iterator ni = this->Names.begin();
+      ni != this->Names.end(); ++ni)
+    {
+    if(cmTarget* target =
+       this->Makefile->GetLocalGenerator()->GetGlobalGenerator()
+       ->FindTarget(0, ni->c_str(), true))
+      {
+      // Handle the current target.
+      if(!this->HandleTarget(target))
+        {
+        return false;
+        }
+      }
+    else
+      {
+      cmOStringStream e;
+      e << "could not find TARGET " << *ni
+        << ".  Perhaps it has not yet been created.";
+      this->SetError(e.str().c_str());
+      return false;
+      }
+    }
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleTarget(cmTarget* target)
+{
+  // Set or append the property.
+  const char* name = this->PropertyName.c_str();
+  std::string value;
+  if(this->ConstructValue(value, target->GetProperty(name)))
+    {
+    // Set the new property.
+    target->SetProperty(name, value.c_str());
+    }
+  else
+    {
+    // Remove the property.
+    target->SetProperty(name, 0);
+    }
+
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleSourceMode()
+{
+  for(std::set<cmStdString>::const_iterator ni = this->Names.begin();
+      ni != this->Names.end(); ++ni)
+    {
+    // Get the source file.
+    if(cmSourceFile* sf = this->Makefile->GetOrCreateSource(ni->c_str()))
+      {
+      if(!this->HandleSource(sf))
+        {
+        return false;
+        }
+      }
+    else
+      {
+      cmOStringStream e;
+      e << "given SOURCE name that could not be found or created: " << *ni;
+      this->SetError(e.str().c_str());
+      return false;
+      }
+    }
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleSource(cmSourceFile* sf)
+{
+  // Set or append the property.
+  const char* name = this->PropertyName.c_str();
+  std::string value;
+  if(this->ConstructValue(value, sf->GetProperty(name)))
+    {
+    // Set the new property.
+    sf->SetProperty(name, value.c_str());
+    }
+  else
+    {
+    // Remove the property.
+    sf->SetProperty(name, 0);
+    }
+
+  // TODO: MACOSX_PACKAGE_LOCATION special case in
+  // cmSetSourceFilesPropertiesCommand
+  // The logic should be moved to cmSourceFile.
+
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleTestMode()
+{
+  // Loop over all tests looking for matching names.
+  std::vector<cmTest*> const& tests = *this->Makefile->GetTests();
+  for(std::vector<cmTest*>::const_iterator ti = tests.begin();
+      ti != tests.end(); ++ti)
+    {
+    cmTest* test = *ti;
+    std::set<cmStdString>::const_iterator ni =
+      this->Names.find(test->GetName());
+    if(ni != this->Names.end())
+      {
+      if(this->HandleTest(test))
+        {
+        this->Names.erase(ni);
+        }
+      else
+        {
+        return false;
+        }
+      }
+    }
+
+  // Names that are still left were not found.
+  if(!this->Names.empty())
+    {
+    cmOStringStream e;
+    e << "given TEST names that do not exist:\n";
+    for(std::set<cmStdString>::const_iterator ni = this->Names.begin();
+        ni != this->Names.end(); ++ni)
+      {
+      e << "  " << *ni << "\n";
+      }
+    this->SetError(e.str().c_str());
+    return false;
+    }
+  return true;
+}
+
+//----------------------------------------------------------------------------
+bool cmSetPropertyCommand::HandleTest(cmTest* test)
+{
+  // Set or append the property.
+  const char* name = this->PropertyName.c_str();
+  std::string value;
+  if(this->ConstructValue(value, test->GetProperty(name)))
+    {
+    // Set the new property.
+    test->SetProperty(name, value.c_str());
+    }
+  else
+    {
+    // Remove the property.
+    test->SetProperty(name, 0);
+    }
+
+  return true;
+}

+ 112 - 0
Source/cmSetPropertyCommand.h

@@ -0,0 +1,112 @@
+/*=========================================================================
+
+  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 cmSetsPropertiesCommand_h
+#define cmSetsPropertiesCommand_h
+
+#include "cmCommand.h"
+
+class cmSetPropertyCommand : public cmCommand
+{
+public:
+  cmSetPropertyCommand();
+
+  virtual cmCommand* Clone()
+    {
+      return new cmSetPropertyCommand;
+    }
+
+  /**
+   * This is called when the command is first encountered in
+   * the input file.
+   */
+  virtual bool InitialPass(std::vector<std::string> const& args);
+
+  /**
+   * The name of the command as specified in CMakeList.txt.
+   */
+  virtual const char* GetName() { return "set_property";}
+
+  /**
+   * Succinct documentation.
+   */
+  virtual const char* GetTerseDocumentation()
+    {
+    return "Set a named property in a given scope.";
+    }
+
+  /**
+   * Longer documentation.
+   */
+  virtual const char* GetFullDocumentation()
+    {
+      return
+        "  set_property(<GLOBAL                            |\n"
+        "                DIRECTORY [dir]                   |\n"
+        "                TARGET    [target1 [target2 ...]] |\n"
+        "                SOURCE    [src1 [src2 ...]]       |\n"
+        "                TEST      [test1 [test2 ...]]>\n"
+        "               [APPEND]\n"
+        "               PROPERTY <name> [value1 [value2 ...]])\n"
+        "Set one property on zero or more objects of a scope.  "
+        "The first argument determines the scope in which the property "
+        "is set.  It must be one of the following:\n"
+        "GLOBAL scope is unique and does not accept a name.\n"
+        "DIRECTORY scope defaults to the current directory but another "
+        "directory (already processed by CMake) may be named by full or "
+        "relative path.\n"
+        "TARGET scope may name zero or more existing targets.\n"
+        "SOURCE scope may name zero or more source files.\n"
+        "TEST scope may name zero or more existing tests.\n"
+        "The required PROPERTY option is immediately followed by the name "
+        "of the property to set.  Remaining arguments are used to "
+        "compose the property value in the form of a semicolon-separated "
+        "list.  "
+        "If the APPEND option is given the list is appended to any "
+        "existing property value."
+        ;
+    }
+
+  /**
+   * This determines if the command is invoked when in script mode.
+   */
+  virtual bool IsScriptable() { return true; }
+
+  cmTypeMacro(cmSetPropertyCommand, cmCommand);
+
+private:
+  std::set<cmStdString> Names;
+  std::string PropertyName;
+  std::string PropertyValue;
+  bool AppendMode;
+
+  // Implementation of value construction.
+  bool ConstructValue(std::string& value, const char* old);
+
+  // Implementation of each property type.
+  bool HandleGlobalMode();
+  bool HandleDirectoryMode();
+  bool HandleTargetMode();
+  bool HandleTarget(cmTarget* target);
+  bool HandleSourceMode();
+  bool HandleSource(cmSourceFile* sf);
+  bool HandleTestMode();
+  bool HandleTest(cmTest* test);
+};
+
+
+
+#endif

+ 1 - 1
Tests/DocTest/CMakeLists.txt

@@ -2,5 +2,5 @@ project (DocTest)
 
 add_executable (DocTest DocTest.cxx)
 
-set_properties(GLOBAL PROPERTIES REPORT_UNDEFINED_PROPERTIES
+set_property(GLOBAL PROPERTY REPORT_UNDEFINED_PROPERTIES
   "${CMAKE_CURRENT_BINARY_DIR}/UndefinedProperties.txt")

+ 4 - 5
Tests/Properties/CMakeLists.txt

@@ -24,10 +24,9 @@ include_directories("${Properties_SOURCE_DIR}" "${Properties_BINARY_DIR}")
 # test generic property interfaces
 define_property(GLOBALTEST GLOBAL "A test property" 
   "A long description of this test property" 0)
-set_properties(GLOBAL PROPERTIES GLOBALTEST 1)
-set_properties(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
-  PROPERTIES DIRECTORYTEST 1)
-set_properties(SOURCE_FILE SubDir/properties3.cxx PROPERTIES SOURCETEST 1)
+set_property(GLOBAL PROPERTY GLOBALTEST 1)
+set_property(DIRECTORY PROPERTY DIRECTORYTEST 1)
+set_property(SOURCE SubDir/properties3.cxx PROPERTY SOURCETEST 1)
 get_property(GLOBALRESULT GLOBAL GLOBALTEST)
 get_property(DIRECTORYRESULT DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 
   DIRECTORYTEST)
@@ -46,7 +45,7 @@ endif (RESULT1 AND RESULT2 AND RESULT3 AND GLOBALRESULT AND
   DIRECTORYRESULT AND SOURCE_FILERESULT)
 
 # test the target property
-set_properties(TARGET Properties PROPERTIES TARGETTEST 1)
+set_property(TARGET Properties PROPERTY TARGETTEST 1)
 get_property(TARGETRESULT TARGET Properties TARGETTEST)
 if (NOT TARGETRESULT)
     message("Error: target result is TARGETRESULT=${TARGETRESULT}")