mirror of https://github.com/mongodb/mongo
SERVER-75613 Add GDB pretty printers for immutable data structures
(cherry picked from commit 98d43cc794)
GitOrigin-RevId: fc6b7b4cb4fd271cef7abdbe43376e166002a19c
This commit is contained in:
parent
38a4eca7c0
commit
defe45e44c
3
.gdbinit
3
.gdbinit
|
|
@ -13,3 +13,6 @@ source buildscripts/gdb/mongo_lock.py
|
|||
|
||||
# Load methods for printing in-memory contents of WT tables.
|
||||
source buildscripts/gdb/wt_dump_table.py
|
||||
|
||||
# Load third-party pretty printers
|
||||
source src/third_party/immer/dist/tools/gdb_pretty_printers/autoload.py
|
||||
|
|
|
|||
|
|
@ -9,8 +9,12 @@ from pathlib import Path
|
|||
import gdb
|
||||
import gdb.printing
|
||||
|
||||
ROOT_PATH = str(Path(os.path.abspath(__file__)).parent.parent.parent)
|
||||
if ROOT_PATH not in sys.path:
|
||||
sys.path.insert(0, ROOT_PATH)
|
||||
from src.third_party.immer.dist.tools.gdb_pretty_printers.printers import ListIter as ImmerListIter # pylint: disable=wrong-import-position
|
||||
|
||||
if not gdb:
|
||||
sys.path.insert(0, str(Path(os.path.abspath(__file__)).parent.parent.parent))
|
||||
from buildscripts.gdb.mongo import get_boost_optional
|
||||
from buildscripts.gdb.optimizer_printers import register_abt_printers
|
||||
|
||||
|
|
@ -600,6 +604,61 @@ class AbslFlatHashMapPrinter(AbslHashMapPrinterBase):
|
|||
yield ('value', kvp['value'])
|
||||
|
||||
|
||||
class ImmutableMapIter(ImmerListIter):
|
||||
def __init__(self, val):
|
||||
super().__init__(val)
|
||||
self.max = (1 << 64) - 1
|
||||
self.pair = None
|
||||
self.curr = (None, self.max, self.max)
|
||||
|
||||
def __next__(self):
|
||||
if self.pair:
|
||||
result = ('value', self.pair['second'])
|
||||
self.pair = None
|
||||
self.i += 1
|
||||
return result
|
||||
if self.i == self.size:
|
||||
raise StopIteration
|
||||
if self.i < self.curr[1] or self.i >= self.curr[2]:
|
||||
self.curr = self.region()
|
||||
self.pair = self.curr[0][self.i - self.curr[1]].cast(
|
||||
gdb.lookup_type(self.v.type.template_argument(0).name))
|
||||
result = ('key', self.pair['first'])
|
||||
return result
|
||||
|
||||
|
||||
class ImmutableMapPrinter:
|
||||
"""Pretty-printer for mongo::immutable::map<>."""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return '%s of size %d' % (self.val.type, int(self.val['_storage']['impl_']['size']))
|
||||
|
||||
def children(self):
|
||||
return ImmutableMapIter(self.val['_storage'])
|
||||
|
||||
def display_hint(self):
|
||||
return 'map'
|
||||
|
||||
|
||||
class ImmutableSetPrinter:
|
||||
"""Pretty-printer for mongo::immutable::set<>."""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return '%s of size %d' % (self.val.type, int(self.val['_storage']['impl_']['size']))
|
||||
|
||||
def children(self):
|
||||
return ImmerListIter(self.val['_storage'])
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
def find_match_brackets(search, opening='<', closing='>'):
|
||||
"""Return the index of the closing bracket that matches the first opening bracket.
|
||||
|
||||
|
|
@ -925,6 +984,8 @@ def build_pretty_printer():
|
|||
pp.add('__wt_update', '__wt_update', False, WtUpdateToBsonPrinter)
|
||||
pp.add('CodeFragment', 'mongo::sbe::vm::CodeFragment', False, SbeCodeFragmentPrinter)
|
||||
pp.add('boost::optional', 'boost::optional', True, BoostOptionalPrinter)
|
||||
pp.add('immutable::map', 'mongo::immutable::map', True, ImmutableMapPrinter)
|
||||
pp.add('immutable::set', 'mongo::immutable::set', True, ImmutableSetPrinter)
|
||||
|
||||
# Optimizer/ABT related pretty printers that can be used only with a running process.
|
||||
register_abt_printers(pp)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.. image:: https://github.com/arximboldi/immer/workflows/test/badge.svg
|
||||
:target: https://github.com/arximboldi/immer/actions?query=workflow%3Atest+branch%3Amaster
|
||||
:alt: Github Actions Badge
|
||||
:alt: GitHub Actions Badge
|
||||
|
||||
.. image:: https://codecov.io/gh/arximboldi/immer/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/arximboldi/immer
|
||||
|
|
@ -74,7 +74,7 @@ Example
|
|||
For a **complete example** check `Ewig, a simple didactic
|
||||
text-editor <https://github.com/arximboldi/ewig>`_ built with this
|
||||
library. You may also wanna check `Lager, a Redux-like library
|
||||
<https://github.com/arximboldi/lager>`_ for writting interactive
|
||||
<https://github.com/arximboldi/lager>`_ for writing interactive
|
||||
software in C++ using a value-oriented design.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# empty
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import gdb.printing
|
||||
import os
|
||||
|
||||
path = os.path.dirname(__file__)
|
||||
if not path in sys.path:
|
||||
sys.path.append(path)
|
||||
from printers import immer_lookup_function
|
||||
|
||||
gdb.printing.register_pretty_printer(gdb.current_objfile(), immer_lookup_function)
|
||||
|
||||
print("immer gdb pretty-printers loaded")
|
||||
|
|
@ -0,0 +1,485 @@
|
|||
# Sourced from https://gist.github.com/dwightguth/283afe96b60b3793f3c02036701457f8
|
||||
# with light modifications.
|
||||
|
||||
import gdb.printing
|
||||
import decimal
|
||||
import traceback
|
||||
import re
|
||||
|
||||
MAX = 1 << 64 - 1
|
||||
|
||||
|
||||
class ArrayIter:
|
||||
def __init__(self, val):
|
||||
self.val_ptr = val.type.template_argument(0).pointer()
|
||||
self.v = val['impl_']
|
||||
self.size = self.v['size']
|
||||
self.i = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.i == self.size:
|
||||
raise StopIteration
|
||||
ptr = self.v['ptr']
|
||||
data = ptr.dereference()['impl']['d']['buffer'].address.reinterpret_cast(self.val_ptr)
|
||||
result = ('[%d]' % self.i, data[self.i])
|
||||
self.i += 1
|
||||
return result
|
||||
|
||||
|
||||
class Relaxed:
|
||||
def __init__(self, node, shift, relaxed, it):
|
||||
self.node = node
|
||||
self.shift = shift
|
||||
self.relaxed = relaxed
|
||||
self.it = it
|
||||
self.B = self.node.type.target().template_argument(2)
|
||||
self.BL = self.node.type.target().template_argument(3)
|
||||
|
||||
def index(self, idx):
|
||||
offset = idx >> self.shift
|
||||
while self.relaxed.dereference()['d']['sizes'][offset] <= idx:
|
||||
offset += 1
|
||||
return offset
|
||||
|
||||
def towards(self, idx):
|
||||
offset = self.index(idx)
|
||||
left_size = self.relaxed.dereference()['d']['sizes'][offset - 1] if offset else 0
|
||||
child = self.it.inner(self.node)[offset]
|
||||
is_leaf = self.shift == self.BL
|
||||
next_size = self.relaxed.dereference()['d']['sizes'][offset] - left_size
|
||||
next_idx = idx - left_size
|
||||
if is_leaf:
|
||||
return self.it.visit_leaf(LeafSub(child, next_size), next_idx)
|
||||
else:
|
||||
return self.it.visit_maybe_relaxed_sub(child, self.shift - self.B, next_size, next_idx)
|
||||
|
||||
|
||||
class LeafSub:
|
||||
def __init__(self, node, count):
|
||||
self.node = node
|
||||
self.count_ = count
|
||||
self.BL = self.node.type.target().template_argument(3)
|
||||
self.MASK = (1 << self.BL) - 1
|
||||
|
||||
def index(self, idx):
|
||||
return idx & self.MASK
|
||||
|
||||
def count(self):
|
||||
return self.count_
|
||||
|
||||
|
||||
class FullLeaf:
|
||||
def __init__(self, node):
|
||||
self.node = node
|
||||
self.BL = self.node.type.target().template_argument(3)
|
||||
self.BRANCHES = 1 << self.BL
|
||||
self.MASK = self.BRANCHES - 1
|
||||
|
||||
def index(self, idx):
|
||||
return idx & self.MASK
|
||||
|
||||
def count(self):
|
||||
return self.BRANCHES
|
||||
|
||||
|
||||
class Leaf:
|
||||
def __init__(self, node, size):
|
||||
self.node = node
|
||||
self.size = size
|
||||
self.BL = self.node.type.target().template_argument(3)
|
||||
self.MASK = (1 << self.BL) - 1
|
||||
|
||||
def index(self, idx):
|
||||
return idx & self.MASK
|
||||
|
||||
def count(self):
|
||||
return self.index(self.size - 1) + 1
|
||||
|
||||
|
||||
class RegularSub:
|
||||
def __init__(self, node, shift, size, it):
|
||||
self.node = node
|
||||
self.shift = shift
|
||||
self.size = size
|
||||
self.it = it
|
||||
self.B = self.node.type.target().template_argument(2)
|
||||
self.MASK = (1 << self.B) - 1
|
||||
|
||||
def towards(self, idx):
|
||||
offset = self.index(idx)
|
||||
count = self.count()
|
||||
return self.it.towards_regular(self, idx, offset, count)
|
||||
|
||||
def index(self, idx):
|
||||
return (idx >> self.shift) & self.MASK
|
||||
|
||||
def count(self):
|
||||
return self.subindex(self.size - 1) + 1
|
||||
|
||||
def subindex(self, idx):
|
||||
return idx >> self.shift
|
||||
|
||||
|
||||
class Regular:
|
||||
def __init__(self, node, shift, size, it):
|
||||
self.node = node
|
||||
self.shift = shift
|
||||
self.size = size
|
||||
self.it = it
|
||||
self.B = self.node.type.target().template_argument(2)
|
||||
self.MASK = (1 << self.B) - 1
|
||||
|
||||
def index(self, idx):
|
||||
return (idx >> self.shift) & self.MASK
|
||||
|
||||
def count(self):
|
||||
return self.index(self.size - 1) + 1
|
||||
|
||||
def towards(self, idx):
|
||||
offset = self.index(idx)
|
||||
count = self.count()
|
||||
return self.it.towards_regular(self, idx, offset, count)
|
||||
|
||||
|
||||
class Full:
|
||||
def __init__(self, node, shift, it):
|
||||
self.node = node
|
||||
self.shift = shift
|
||||
self.it = it
|
||||
self.B = self.node.type.target().template_argument(2)
|
||||
self.BL = self.node.type.target().template_argument(3)
|
||||
self.MASK = (1 << self.B) - 1
|
||||
|
||||
def index(self, idx):
|
||||
return (idx >> self.shift) & self.MASK
|
||||
|
||||
def towards(self, idx):
|
||||
offset = self.index(idx)
|
||||
is_leaf = self.shift == self.BL
|
||||
child = self.it.inner(self.node)[offset]
|
||||
if is_leaf:
|
||||
return self.it.visit_leaf(FullLeaf(child), idx)
|
||||
else:
|
||||
return Full(child, self.shift - self.B, self.it).towards(idx)
|
||||
|
||||
|
||||
class ListIter:
|
||||
def __init__(self, val):
|
||||
self.v = val['impl_']
|
||||
self.size = self.v['size']
|
||||
self.i = 0
|
||||
self.curr = (None, MAX, MAX)
|
||||
self.node_ptr_ptr = self.v['root'].type.pointer()
|
||||
self.B = self.v['root'].type.target().template_argument(2)
|
||||
self.BL = self.v['root'].type.target().template_argument(3)
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.i == self.size:
|
||||
raise StopIteration
|
||||
if self.i < self.curr[1] or self.i >= self.curr[2]:
|
||||
self.curr = self.region()
|
||||
result = ('[%d]' % self.i, self.curr[0][self.i - self.curr[1]].cast(
|
||||
gdb.lookup_type(self.v.type.template_argument(0).name)))
|
||||
self.i += 1
|
||||
return result
|
||||
|
||||
def region(self):
|
||||
tail_off = self.tail_offset()
|
||||
if self.i >= tail_off:
|
||||
return (self.leaf(self.v['tail']), tail_off, self.size)
|
||||
else:
|
||||
subs = self.visit_maybe_relaxed_sub(self.v['root'], self.v['shift'], tail_off, self.i)
|
||||
first = self.i - subs[1]
|
||||
end = first + subs[2]
|
||||
return (subs[0], first, end)
|
||||
|
||||
def tail_offset(self):
|
||||
r = self.relaxed(self.v['root'])
|
||||
if r:
|
||||
return r.dereference()['d']['sizes'][r.dereference()['d']['count'] - 1]
|
||||
elif self.size:
|
||||
return (self.size - 1) & ~self.leaf_mask()
|
||||
else:
|
||||
return 0
|
||||
|
||||
def relaxed(self, node):
|
||||
return node.dereference()['impl']['d']['data']['inner']['relaxed']
|
||||
|
||||
def leaf(self, node):
|
||||
return node.dereference()['impl']['d']['data']['leaf']['buffer'].address
|
||||
|
||||
def inner(self, node):
|
||||
return node.dereference()['impl']['d']['data']['inner']['buffer'].address.reinterpret_cast(
|
||||
self.node_ptr_ptr)
|
||||
|
||||
def visit_maybe_relaxed_sub(self, node, shift, size, idx):
|
||||
relaxed = self.relaxed(node)
|
||||
if relaxed:
|
||||
return Relaxed(node, shift, relaxed, self).towards(idx)
|
||||
else:
|
||||
return RegularSub(node, shift, size, self).towards(idx)
|
||||
|
||||
def visit_leaf(self, pos, idx):
|
||||
return (self.leaf(pos.node), pos.index(idx), pos.count())
|
||||
|
||||
# pos = node, idx = full, offset = shifted & masked, count = shifted
|
||||
def towards_regular(self, pos, idx, offset, count):
|
||||
is_leaf = pos.shift == self.BL
|
||||
child = self.inner(pos.node)[offset]
|
||||
is_full = offset + 1 != count
|
||||
if is_full:
|
||||
if is_leaf:
|
||||
return self.visit_leaf(FullLeaf(child), idx)
|
||||
else:
|
||||
return Full(child, pos.shift - self.B, self).towards(idx)
|
||||
elif is_leaf:
|
||||
return self.visit_leaf(Leaf(child, pos.size), idx)
|
||||
else:
|
||||
return Regular(child, pos.shift - self.B, pos.size, self).towards(idx)
|
||||
|
||||
def leaf_mask(self):
|
||||
return (1 << self.BL) - 1
|
||||
|
||||
|
||||
def popcount(x):
|
||||
b = 0
|
||||
while x > 0:
|
||||
x &= x - 1
|
||||
b += 1
|
||||
return b
|
||||
|
||||
|
||||
class ChampIter:
|
||||
def __init__(self, val):
|
||||
self.depth = 0
|
||||
self.count = 0
|
||||
v = val['impl_']['root']
|
||||
self.node_ptr_ptr = v.type.pointer()
|
||||
m = self.datamap(v)
|
||||
if m:
|
||||
self.cur = self.values(v)
|
||||
self.end = self.values(v) + popcount(m)
|
||||
else:
|
||||
self.cur = None
|
||||
self.end = None
|
||||
self.path = [v.address]
|
||||
self.B = v.type.target().template_argument(4)
|
||||
self.MAX_DEPTH = ((8 * 8) + self.B - 1) / 8
|
||||
self.ensure_valid()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.cur == None:
|
||||
raise StopIteration
|
||||
result = self.cur.dereference()
|
||||
self.cur += 1
|
||||
self.count += 1
|
||||
self.ensure_valid()
|
||||
return result
|
||||
|
||||
def ensure_valid(self):
|
||||
while self.cur == self.end:
|
||||
while self.step_down():
|
||||
if self.cur != self.end:
|
||||
return
|
||||
if not self.step_right():
|
||||
self.cur = None
|
||||
self.end = None
|
||||
return
|
||||
|
||||
def step_down(self):
|
||||
if self.depth < self.MAX_DEPTH:
|
||||
parent = self.path[self.depth].dereference()
|
||||
if self.nodemap(parent):
|
||||
self.depth += 1
|
||||
self.path.append(self.children(parent))
|
||||
child = self.path[self.depth]
|
||||
if self.depth < self.MAX_DEPTH:
|
||||
m = self.datamap(child)
|
||||
if m:
|
||||
self.cur = self.values(child)
|
||||
self.end = self.cur + popcount(m)
|
||||
else:
|
||||
self.cur = self.collisions(child)
|
||||
self.end = self.cur = self.collision_count(child)
|
||||
return True
|
||||
return False
|
||||
|
||||
def step_right(self):
|
||||
while self.depth > 0:
|
||||
parent = self.path[self.depth - 1].dereference()
|
||||
last = self.children(parent) + popcount(self.nodemap(parent))
|
||||
next_ = self.path[self.depth] + 1
|
||||
if next_ < last:
|
||||
self.path[self.depth] = next_
|
||||
child = self.path[self.depth].dereference()
|
||||
if self.depth < self.MAX_DEPTH:
|
||||
m = self.datamap(child)
|
||||
if m:
|
||||
self.cur = self.values(child)
|
||||
self.end = self.cur + popcount(m)
|
||||
else:
|
||||
self.cur = self.collisions(child)
|
||||
self.end = self.cur + self.collision_count(child)
|
||||
return True
|
||||
self.depth -= 1
|
||||
self.path.pop()
|
||||
return False
|
||||
|
||||
def values(self, node):
|
||||
return node.dereference()['impl']['d']['data']['inner']['values'].dereference(
|
||||
)['d']['buffer'].address.cast(self.T_ptr)
|
||||
|
||||
def children(self, node):
|
||||
return node.dereference()['impl']['d']['data']['inner']['buffer'].address.cast(
|
||||
self.node_ptr_ptr)
|
||||
|
||||
def datamap(self, node):
|
||||
return node.dereference()['impl']['d']['data']['inner']['datamap']
|
||||
|
||||
def nodemap(self, node):
|
||||
return node.dereference()['impl']['d']['data']['inner']['nodemap']
|
||||
|
||||
def collision_count(self, node):
|
||||
return node.dereference()['impl']['d']['data']['collision']['count']
|
||||
|
||||
def collisions(self, node):
|
||||
return node.dereference()['impl']['d']['data']['collision']['buffer'].address.cast(
|
||||
self.T_ptr)
|
||||
|
||||
|
||||
class MapIter(ChampIter):
|
||||
def __init__(self, val):
|
||||
self.T_ptr = gdb.lookup_type("std::pair<" + val.type.template_argument(0).name + ", " +
|
||||
val.type.template_argument(1).name + ">").pointer()
|
||||
ChampIter.__init__(self, val)
|
||||
self.pair = None
|
||||
|
||||
def __next__(self):
|
||||
if self.pair:
|
||||
result = ('[%d]' % self.count, self.pair['second'])
|
||||
self.pair = None
|
||||
return result
|
||||
self.pair = super().__next__()
|
||||
return ('[%d]' % self.count, self.pair['first'])
|
||||
|
||||
|
||||
class SetIter(ChampIter):
|
||||
def __init__(self, val):
|
||||
self.T_ptr = gdb.lookup_type(val.type.template_argument(0).name).pointer()
|
||||
ChampIter.__init__(self, val)
|
||||
|
||||
def __next__(self):
|
||||
return ('[%d]' % self.count, super().__next__())
|
||||
|
||||
|
||||
def num_elements(num):
|
||||
return '1 element' if num == 1 else '%d elements' % num
|
||||
|
||||
|
||||
class ArrayPrinter:
|
||||
"Prints an immer::array"
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return 'immer::array with %s' % num_elements(self.val['impl_']['size'])
|
||||
|
||||
def children(self):
|
||||
return ArrayIter(self.val)
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
class MapPrinter:
|
||||
"Print an immer::map"
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return 'immer::map with %s' % num_elements(self.val['impl_']['size'])
|
||||
|
||||
def children(self):
|
||||
return MapIter(self.val)
|
||||
|
||||
def display_hint(self):
|
||||
return 'map'
|
||||
|
||||
|
||||
class SetPrinter:
|
||||
"Prints an immer::set"
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return 'immer::set with %s' % num_elements(self.val['impl_']['size'])
|
||||
|
||||
def children(self):
|
||||
return SetIter(self.val)
|
||||
|
||||
|
||||
class TablePrinter:
|
||||
"Prints an immer::table"
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return 'immer::table with %s' % num_elements(self.val['impl_']['size'])
|
||||
|
||||
def children(self):
|
||||
return SetIter(self.val)
|
||||
|
||||
|
||||
class ListPrinter:
|
||||
"Prints an immer::vector or immer::flex_vector"
|
||||
|
||||
def __init__(self, val, typename):
|
||||
self.val = val
|
||||
self.typename = typename
|
||||
|
||||
def to_string(self):
|
||||
return '%s of length %d' % (self.typename, int(self.val['impl_']['size']))
|
||||
|
||||
def children(self):
|
||||
return ListIter(self.val)
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
|
||||
def immer_lookup_function(val):
|
||||
compiled_rx = re.compile('^([a-zA-Z0-9_:]+)(<.*>)?$')
|
||||
typename = gdb.types.get_basic_type(val.type).tag
|
||||
if not typename:
|
||||
return None
|
||||
match = compiled_rx.match(typename)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
basename = match.group(1)
|
||||
if basename == "immer::array":
|
||||
return ArrayPrinter(val)
|
||||
elif basename == "immer::map":
|
||||
return MapPrinter(val)
|
||||
elif basename == "immer::set":
|
||||
return SetPrinter(val)
|
||||
elif basename == "immer::table":
|
||||
return TablePrinter(val)
|
||||
elif basename == "immer::vector":
|
||||
return ListPrinter(val, "immer::vector")
|
||||
elif basename == "immer::flex_vector":
|
||||
return ListPrinter(val, "immer::flex_vector")
|
||||
return None
|
||||
|
|
@ -27,7 +27,13 @@ rm -rf $LIB_GIT_DIR/example
|
|||
rm -rf $LIB_GIT_DIR/extra
|
||||
rm -rf $LIB_GIT_DIR/nix
|
||||
rm -rf $LIB_GIT_DIR/test
|
||||
rm -rf $LIB_GIT_DIR/tools
|
||||
rm -rf $LIB_GIT_DIR/tools/clojure
|
||||
rm -rf $LIB_GIT_DIR/tools/docker
|
||||
rm -rf $LIB_GIT_DIR/tools/include
|
||||
rm -rf $LIB_GIT_DIR/tools/scala
|
||||
rm -rf $LIB_GIT_DIR/tools/sinusoidal-sphinx-theme
|
||||
rm -rf $LIB_GIT_DIR/tools/reproduce-paper-results.bash
|
||||
rm -rf $LIB_GIT_DIR/tools/with-tee.bash
|
||||
rm -f $LIB_GIT_DIR/BUILD
|
||||
rm -f $LIB_GIT_DIR/CMakeLists.txt
|
||||
rm -f $LIB_GIT_DIR/Package.swift
|
||||
|
|
|
|||
Loading…
Reference in New Issue