@@ -1546,13 +1546,27 @@ def _var_to_bool(self, var):
1546
1546
return True
1547
1547
return False
1548
1548
1549
- def _cmake_set (self , tline : CMakeTraceLine ):
1549
+ def _cmake_set (self , tline : CMakeTraceLine ) -> None :
1550
+ """Handler for the CMake set() function in all variaties.
1551
+
1552
+ comes in three flavors:
1553
+ set(<var> <value> [PARENT_SCOPE])
1554
+ set(<var> <value> CACHE <type> <docstring> [FORCE])
1555
+ set(ENV{<var>} <value>)
1556
+
1557
+ We don't support the ENV variant, and any uses of it will be ignored
1558
+ silently. the other two variates are supported, with some caveats:
1559
+ - we don't properly handle scoping, so calls to set() inside a
1560
+ function without PARENT_SCOPE set could incorrectly shadow the
1561
+ outer scope.
1562
+ - We don't honor the type of CACHE arguments
1563
+ """
1550
1564
# DOC: https://cmake.org/cmake/help/latest/command/set.html
1551
1565
1552
1566
# 1st remove PARENT_SCOPE and CACHE from args
1553
1567
args = []
1554
1568
for i in tline .args :
1555
- if i == 'PARENT_SCOPE' or len ( i ) == 0 :
1569
+ if not i or i == 'PARENT_SCOPE' :
1556
1570
continue
1557
1571
1558
1572
# Discard everything after the CACHE keyword
@@ -1564,13 +1578,19 @@ def _cmake_set(self, tline: CMakeTraceLine):
1564
1578
if len (args ) < 1 :
1565
1579
raise self ._gen_exception ('CMake: set() requires at least one argument\n {}' .format (tline ))
1566
1580
1567
- if len (args ) == 1 :
1581
+ # Now that we've removed extra arguments all that should be left is the
1582
+ # variable identifier and the value, join the value back together to
1583
+ # ensure spaces in the value are correctly handled. This assumes that
1584
+ # variable names don't have spaces. Please don't do that...
1585
+ identifier = args .pop (0 )
1586
+ value = ' ' .join (args )
1587
+
1588
+ if not value :
1568
1589
# Same as unset
1569
- if args [ 0 ] in self .vars :
1570
- del self .vars [args [ 0 ] ]
1590
+ if identifier in self .vars :
1591
+ del self .vars [identifier ]
1571
1592
else :
1572
- values = list (itertools .chain (* map (lambda x : x .split (';' ), args [1 :])))
1573
- self .vars [args [0 ]] = values
1593
+ self .vars [identifier ] = value .split (';' )
1574
1594
1575
1595
def _cmake_unset (self , tline : CMakeTraceLine ):
1576
1596
# DOC: https://cmake.org/cmake/help/latest/command/unset.html
@@ -1619,7 +1639,7 @@ def _cmake_add_custom_target(self, tline: CMakeTraceLine):
1619
1639
1620
1640
self .targets [tline .args [0 ]] = CMakeTarget (tline .args [0 ], 'CUSTOM' , {})
1621
1641
1622
- def _cmake_set_property (self , tline : CMakeTraceLine ):
1642
+ def _cmake_set_property (self , tline : CMakeTraceLine ) -> None :
1623
1643
# DOC: https://cmake.org/cmake/help/latest/command/set_property.html
1624
1644
args = list (tline .args )
1625
1645
@@ -1629,8 +1649,10 @@ def _cmake_set_property(self, tline: CMakeTraceLine):
1629
1649
1630
1650
append = False
1631
1651
targets = []
1632
- while len ( args ) > 0 :
1652
+ while args :
1633
1653
curr = args .pop (0 )
1654
+ # XXX: APPEND_STRING is specifically *not* supposed to create a
1655
+ # list, is treating them as aliases really okay?
1634
1656
if curr == 'APPEND' or curr == 'APPEND_STRING' :
1635
1657
append = True
1636
1658
continue
@@ -1640,60 +1662,75 @@ def _cmake_set_property(self, tline: CMakeTraceLine):
1640
1662
1641
1663
targets .append (curr )
1642
1664
1665
+ if not args :
1666
+ raise self ._gen_exception ('CMake: set_property() faild to parse argument list\n {}' .format (tline ))
1667
+
1643
1668
if len (args ) == 1 :
1644
1669
# Tries to set property to nothing so nothing has to be done
1645
1670
return
1646
1671
1647
- if len (args ) < 2 :
1648
- raise self ._gen_exception ('CMake: set_property() faild to parse argument list\n {}' .format (tline ))
1649
-
1650
- propName = args [0 ]
1651
- propVal = list (itertools .chain (* map (lambda x : x .split (';' ), args [1 :])))
1652
- propVal = list (filter (lambda x : len (x ) > 0 , propVal ))
1653
-
1654
- if len (propVal ) == 0 :
1672
+ identifier = args .pop (0 )
1673
+ value = ' ' .join (args ).split (';' )
1674
+ if not value :
1655
1675
return
1656
1676
1657
1677
for i in targets :
1658
1678
if i not in self .targets :
1659
1679
raise self ._gen_exception ('CMake: set_property() TARGET {} not found\n {}' .format (i , tline ))
1660
1680
1661
- if propName not in self .targets [i ].properies :
1662
- self .targets [i ].properies [propName ] = []
1681
+ if identifier not in self .targets [i ].properies :
1682
+ self .targets [i ].properies [identifier ] = []
1663
1683
1664
1684
if append :
1665
- self .targets [i ].properies [propName ] += propVal
1685
+ self .targets [i ].properies [identifier ] += value
1666
1686
else :
1667
- self .targets [i ].properies [propName ] = propVal
1687
+ self .targets [i ].properies [identifier ] = value
1668
1688
1669
- def _cmake_set_target_properties (self , tline : CMakeTraceLine ):
1689
+ def _cmake_set_target_properties (self , tline : CMakeTraceLine ) -> None :
1670
1690
# DOC: https://cmake.org/cmake/help/latest/command/set_target_properties.html
1671
1691
args = list (tline .args )
1672
1692
1673
1693
targets = []
1674
- while len ( args ) > 0 :
1694
+ while args :
1675
1695
curr = args .pop (0 )
1676
1696
if curr == 'PROPERTIES' :
1677
1697
break
1678
1698
1679
1699
targets .append (curr )
1680
1700
1681
- if (len (args ) % 2 ) != 0 :
1682
- raise self ._gen_exception ('CMake: set_target_properties() uneven number of property arguments\n {}' .format (tline ))
1683
-
1684
- while len (args ) > 0 :
1685
- propName = args .pop (0 )
1686
- propVal = args .pop (0 ).split (';' )
1687
- propVal = list (filter (lambda x : len (x ) > 0 , propVal ))
1688
-
1689
- if len (propVal ) == 0 :
1690
- continue
1701
+ # Now we need to try to reconsitute the original quoted format of the
1702
+ # arguments, as a property value could have spaces in it. Unlike
1703
+ # set_property() this is not context free. There are two approaches I
1704
+ # can think of, both have drawbacks:
1705
+ #
1706
+ # 1. Assume that the property will be capitalized, this is convention
1707
+ # but cmake doesn't require it.
1708
+ # 2. Maintain a copy of the list here: https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#target-properties
1709
+ #
1710
+ # Neither of these is awesome for obvious reasons. I'm going to try
1711
+ # option 1 first and fall back to 2, as 1 requires less code and less
1712
+ # synchroniztion for cmake changes.
1713
+
1714
+ arglist = [] # type: typing.List[typing.Tuple[str, typing.List[str]]]
1715
+ name = args .pop (0 )
1716
+ values = []
1717
+ for a in args :
1718
+ if a .isupper ():
1719
+ if values :
1720
+ arglist .append ((name , ' ' .join (values ).split (';' )))
1721
+ name = a
1722
+ values = []
1723
+ else :
1724
+ values .append (a )
1725
+ if values :
1726
+ arglist .append ((name , ' ' .join (values ).split (';' )))
1691
1727
1728
+ for name , value in arglist :
1692
1729
for i in targets :
1693
1730
if i not in self .targets :
1694
1731
raise self ._gen_exception ('CMake: set_target_properties() TARGET {} not found\n {}' .format (i , tline ))
1695
1732
1696
- self .targets [i ].properies [propName ] = propVal
1733
+ self .targets [i ].properies [name ] = value
1697
1734
1698
1735
def _lex_trace (self , trace ):
1699
1736
# The trace format is: '<file>(<line>): <func>(<args -- can contain \n> )\n'
0 commit comments