Add a python script to auto fix CMakeLists files
diff --git a/autofix_cmakelists.py b/autofix_cmakelists.py
new file mode 100755
index 0000000..17ef588
--- /dev/null
+++ b/autofix_cmakelists.py
@@ -0,0 +1,2054 @@
+#!/usr/bin/env python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import os
+import re
+
+from collections import defaultdict
+
+################################################################################
+################################# Configurations ###############################
+################################################################################
+
+# Project name.
+PROJECT_NAME = "quickstep"
+
+# Don't scan these directories.
+EXCLUDED_DIRECTORIES = frozenset(
+    [ "./build",
+      "./cmake",
+      "./parser/preprocessed",
+      "./release",
+      "./storage/tests",
+      "./third_party" ])
+
+# Don't scan these files.
+EXCLUDED_FILES = frozenset(
+    [ "./empty_src.cpp" ])
+
+# Third-party libraries.
+THIRD_PARTY_LIBRARIES = \
+    { "farmhash" : [ "farmhash" ],
+      "gflags" : [ "${GFLAGS_LIB_NAME}" ],
+      "glog" : [ "glog" ],
+      "gtest/gtest.h" : [ "gtest", "gtest_main" ],
+      "gtest/gtest_prod.h" : [ "gtest" ],
+      "re2" : [ "re2" ],
+      "tmb" : [ "tmb" ] }
+
+# Explicitly ignored targets.
+IGNORED_TARGETS = frozenset(
+    [ "quickstep_cli_LineReader",
+      "quickstep_cli_shell",
+      "quickstep_parser_SqlLexer",
+      "quickstep_parser_SqlParser",
+      "quickstep_parser_SqlParserWrapper",
+      "quickstep_threading_ConditionVariable",
+      "quickstep_threading_Mutex",
+      "quickstep_threading_SharedMutex",
+      "quickstep_threading_Thread" ])
+
+# Do not move these targets around.
+ANCHORED_TARGETS = frozenset(
+    [ "quickstep_cli_shell",
+      "quickstep_utility_TextBasedTestDriver"] )
+
+# Do not move these link dependencies around.
+ANCHORED_LINKS = frozenset(
+    [ "quickstep_cli_shell" ] )
+
+# Source code file extensions under consideration.
+SOURCE_CODE_FILE_EXTENSIONS = [ ".hpp", ".cpp" ]
+
+# Maximum number of characters in one line.
+MAX_LINE_WIDTH = 120
+
+# Dummy source file name.
+DUMMY_CPP_FILE_NAME = "empty_src.cpp"
+
+# Whether to auto-fix target names according to default naming rule.
+# E.g. ./types/containers/ColumnVector.*
+#   -> quickstep_types_containers_ColumnVector
+FORCE_DEFAULT_NAMING_RULE_FOR_LIBRARY = True
+FORCE_DEFAULT_NAMING_RULE_FOR_EXECUTABLE = False
+
+
+################################################################################
+############################### Utility Functions ##############################
+################################################################################
+
+def TopDownVisitor(functor):
+  def Wrapper(*args):
+    # First invoke functor.
+    functor(*args)
+
+    # Recursively process all child nodes.
+    for node in args[0].getChildren():
+      getattr(node, functor.__name__)(*list(args)[1:])
+
+  return Wrapper
+
+def BottomUpVisitor(functor):
+  def Wrapper(*args):
+    # Recursively process all child nodes.
+    for node in args[0].getChildren():
+      getattr(node, functor.__name__)(*list(args)[1:])
+
+    # Then invoke functor.
+    functor(*args)
+
+  return Wrapper
+
+def ResolveIndirectFilename(path, filename):
+  if filename.startswith("${CMAKE_CURRENT_SOURCE_DIR}"):
+    return filename.replace("${CMAKE_CURRENT_SOURCE_DIR}", path)
+
+  if filename.startswith("${PROJECT_SOURCE_DIR}"):
+    return filename.replace("${PROJECT_SOURCE_DIR}", ".")
+
+  return None
+
+
+################################################################################
+################################ Utility Classes ###############################
+################################################################################
+
+class LineStream:
+  """A stream of lines
+  """
+
+  def __init__(self, lines):
+    """
+    Parameters
+    ----------
+    lines : List[String]
+    """
+    self.lines = lines
+    self.pos = 0
+
+  def hasNextLine(self):
+    return self.pos < len(self.lines)
+
+  def top(self):
+    return self.lines[self.pos]
+
+  def pop(self):
+    line = self.lines[self.pos]
+    self.pos += 1
+    return line
+
+  def size(self):
+    return len(self.lines)
+
+
+class CMakeGlobalContext:
+  """Context for global information across all cmake files.
+
+  Attributes
+  ----------
+  document : DocumentRoot
+  referenced_files : Dict[String, Bool]
+  file_index : Dict[String, String]
+  target_index : Dict[String, String]
+  dependencies : Dict[String, List[Tuple[String, List[String]]]]
+  prior_dependencies : Dict[String, List[Tuple[String, List[String]]]]
+  modules : Dict[String, List[String]]
+  tests : Dict[String, Bool]
+  subdirectories : List[String]
+  """
+
+  def __init__(self, document):
+    """
+    Parameters
+    ----------
+    document : DocumentRoot
+    """
+    self.document = document
+    self.referenced_files = {}
+    self.file_index = {}
+    self.target_index = {}
+    self.dependencies = defaultdict(list)
+    self.prior_dependencies = defaultdict(list)
+    self.modules = defaultdict(list)
+    self.tests = {}
+    self.subdirectories = []
+
+  def addFileReference(self, filepath):
+    """
+    Parameters
+    ----------
+    filepath : String
+    """
+    self.referenced_files[filepath] = True
+
+
+class CMakeLocalContext:
+  """Context for processing a cmake file
+
+  Attributes
+  ----------
+  sources : Dict[String, SourceGroup]
+  """
+
+  def __init__(self, sources):
+    """
+    Parameters
+    ----------
+    sources : Dict[String, SourceGroup]
+    """
+    self.sources = sources
+
+
+class CMakeFormat:
+  """Format information of a cmake node
+
+  Attributes
+  ----------
+  indents : Int
+  """
+
+  def __init__(self):
+    self.indents = 0
+
+  def setIndentation(self, indents):
+    """
+    Parameters
+    ----------
+    indents : Int
+    """
+    self.indents = indents
+
+  def getIndentationString(self):
+    """
+    Returns
+    -------
+    String
+    """
+    return " " * self.indents
+
+
+################################################################################
+################# CMake Command Intermediate Representations ###################
+################################################################################
+
+class CMakeNode(object):
+  """Base class for various cmake nodes
+
+  Attributes
+  ----------
+  mutated : Bool
+  """
+
+  def __init__(self):
+    self.mutated = False
+
+  def setMutated(self, mutated):
+    """
+    Parameters
+    ----------
+    mutated : Bool
+    """
+    self.mutated = mutated
+
+  def isMutated(self):
+    """
+    Returns
+    -------
+    Bool
+    """
+    return self.mutated
+
+  def getChildren(self):
+    """
+    Returns
+    -------
+    List[CMakeNode]
+    """
+    return []
+
+  @staticmethod
+  def GenerateDefaultTarget(path, name = None):
+    """
+    Parameters
+    ----------
+    path : String
+    name : String
+
+    Returns
+    -------
+    String
+    """
+    target = PROJECT_NAME + path[1:].replace("_", "").replace("/", "_")
+    if name is not None:
+      target += "_" + name
+    return target
+
+  @TopDownVisitor
+  def autofixTargets(self, local_ctx):
+    """
+    Parameters
+    ----------
+    local_ctx : CMakeLocalContext
+    """
+    pass
+
+  @TopDownVisitor
+  def collectReferencedFiles(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    pass
+
+  @TopDownVisitor
+  def buildTargetIndex(self, path, output):
+    """
+    Parameters
+    ----------
+    path : String
+    output : Dict[String, String]
+    """
+    pass
+
+  @TopDownVisitor
+  def collectTargets(self, output):
+    """
+    Parameters
+    ----------
+    output : List[CMakeTarget]
+    """
+    pass
+
+  @TopDownVisitor
+  def collectLinks(self, output):
+    """
+    Parameters
+    ----------
+    output : List[CMakeLink]
+    """
+    pass
+
+  @TopDownVisitor
+  def collectSubdirectories(self, output):
+    """
+    Parameters
+    ----------
+    output : List[CMakeSubdirectory]
+    """
+    pass
+
+  @TopDownVisitor
+  def collectDependencies(self, path, global_ctx, conditions):
+    """
+    Parameters
+    ----------
+    path : String
+    global_ctx : CMakeGlobalContext
+    conditions : List[String]
+    """
+    pass
+
+  @TopDownVisitor
+  def collectTests(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    pass
+
+  @TopDownVisitor
+  def autofixDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    local_ctx : CMakeLocalContext
+    """
+    pass
+
+
+class CMakeApacheLicense(CMakeNode):
+  """Apache license header
+  """
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+
+    header = """# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License."""
+
+    output.append(header + "\n")
+
+
+class CMakeLine(CMakeNode):
+  """An unrecognized cmake line
+
+  Attributes
+  ----------
+  line : String
+  """
+
+  def __init__(self, stream):
+    """
+    Parameters
+    ----------
+    stream : LineStream
+    """
+    super(CMakeLine, self).__init__()
+
+    if stream is None:
+      return
+
+    self.line = stream.pop().rstrip()
+
+  @staticmethod
+  def CreateEmptyLine():
+    node = CMakeLine(None)
+    node.line = ""
+    return node
+
+  @staticmethod
+  def CreateLine(line):
+    node = CMakeLine(None)
+    node.line = line
+    return node
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    output.append(self.line + "\n")
+
+
+class CMakeIf(CMakeNode):
+  """A cmake if() command
+
+  Attributes
+  ----------
+  condition : String
+  nodes : List[CMakeNode]
+  """
+
+  def __init__(self, stream):
+    """
+    Parameters
+    ----------
+    stream : LineStream
+    """
+    super(CMakeIf, self).__init__()
+
+    depth = 0
+    lines = []
+    while stream.hasNextLine():
+      line = stream.pop()
+      lines.append(line)
+
+      if "endif(" in line or "endif (" in line:
+        depth -= 1
+        if depth == 0:
+          break;
+      else:
+        line = line.strip()
+        if line.startswith("if") and not line.endswith("{"):
+          depth += 1
+
+    self.nodes = []
+
+    if len(lines) == 0:
+      return
+
+    stmt = lines[0]
+    self.condition = stmt[stmt.find('(') + 1 : stmt.find(')')].strip()
+
+    self.nodes.append(CMakeLine.CreateLine(lines[0].rstrip()))
+    self.nodes.append(CMakeGroup(LineStream(lines[1:-1])))
+
+    if len(lines) < 2:
+      return
+
+    self.nodes.append(CMakeLine.CreateLine(lines[-1].rstrip()))
+
+  def getChildren(self):
+    """
+    Returns
+    -------
+    List[CMakeNode]
+    """
+    return self.nodes
+
+  def autofixTargets(self, local_ctx):
+    """Conditional targets require further semantic analysis.
+    """
+    pass
+
+  def collectTargets(self, output):
+    """Conditional targets require further semantic analysis.
+    """
+    pass
+
+  def collectLinks(self, output):
+    """Conditional targets require further semantic analysis.
+    """
+    pass
+
+  def collectSubdirectories(self, output):
+    """
+    Parameters
+    ----------
+    output : List[CMakeDirectory]
+    """
+    for node in self.nodes:
+      node.collectSubdirectories(output)
+
+  def collectDependencies(self, path, global_ctx, conditions):
+    """
+    Parameters
+    ----------
+    path : String
+    global_ctx : CMakeGlobalContext
+    conditions : List[String]
+    """
+    child_conds = conditions + [self.condition]
+    for node in self.nodes:
+      node.collectDependencies(path, global_ctx, child_conds)
+
+  def collectTests(self, global_ctx):
+    """Conditional targets require further semantic analysis.
+    """
+    pass
+
+  def autofixDependencies(self, global_ctx):
+    """Conditional targets require further semantic analysis.
+    """
+    pass
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    for node in self.nodes:
+      node.writeToStream(output)
+
+
+class CMakeSubdirectory(CMakeNode):
+  """A cmake add_subdirectory() command
+
+  Attributes
+  ----------
+  format : CMakeFormat
+  name : String
+  arguments : List[String]
+  multiline : Bool
+  """
+
+  # Static member variables
+  COMMAND_SPLIT_PATTERN = re.compile("[ \t\r\n]+")
+
+  def __init__(self, stream):
+    """
+    Parameters
+    ----------
+    stream : LineStream
+    """
+    super(CMakeSubdirectory, self).__init__()
+
+    if stream is None:
+      return
+
+    stmt = ""
+    while stream.hasNextLine():
+      line = stream.pop()
+      stmt += line
+
+      # NOTE(jianqiao): We assume that the original CMakeLists.txt file
+      # is relatively "well written".
+      if ")" in line:
+        break
+
+    self.format = CMakeFormat()
+    self.format.setIndentation(len(stmt) - len(stmt.lstrip()))
+
+    # Extract the content
+    stmt = stmt[stmt.find('(') + 1 : stmt.find(')')]
+
+    self.multiline = "\n" in stmt
+
+    # Split into target + file list
+    items = CMakeTarget.COMMAND_SPLIT_PATTERN.split(stmt)
+
+    self.name = items[0]
+    self.arguments = items[1:]
+
+  @staticmethod
+  def CreateDirectory(name):
+    node = CMakeSubdirectory(None)
+    node.format = CMakeFormat()
+    node.name = name
+    node.arguments = []
+    node.multiline = False
+    return node
+
+  def collectSubdirectories(self, output):
+    """
+    Parameters
+    ----------
+    output : List[CMakeDirectory]
+    """
+    output.append(self)
+
+  def isRegular(self):
+    if self.name is None:
+      return False
+
+    return not (self.name.startswith("\"") or self.name.startswith("$"))
+
+  def toStringSingleLine(self):
+    """
+    Returns
+    -------
+    String
+    """
+    return self.format.getIndentationString() + "add_subdirectory(" + \
+           " ".join([self.name] + self.arguments) + ")"
+
+  def toStringMultipleLines(self):
+    """
+    Returns
+    -------
+    String
+    """
+    output = self.format.getIndentationString() + "add_subdirectory("
+    spaces = "\n" + " " * len(output)
+    output += spaces.join([self.name] + self.arguments) + ")"
+    return output
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    if self.name is None:
+      return
+
+    item = self.toStringSingleLine()
+
+    # Use multi-line format if the single-line output is too long
+    # or the original command is in multi-line mode and is not mutated.
+    if (len(item) > MAX_LINE_WIDTH
+        or (self.multiline and not self.isMutated())):
+      item = self.toStringMultipleLines()
+
+    output.append(item + "\n")
+
+
+class CMakeTest(CMakeNode):
+  """A cmake add_test() command
+
+  Attributes
+  ----------
+  format : CMakeFormat
+  target : String
+  dependencies : List[String]
+  multiline: Bool
+  """
+
+  # Static member variables
+  COMMAND_SPLIT_PATTERN = re.compile("[ \t\r\n]+")
+
+  def __init__(self, stream):
+    """
+    Parameters
+    ----------
+    stream : LineStream
+    """
+    super(CMakeTest, self).__init__()
+
+    stmt = ""
+    while stream.hasNextLine():
+      line = stream.pop()
+      stmt += line
+
+      # NOTE(jianqiao): We assume that the original CMakeLists.txt file
+      # is relatively "well written".
+      if ")" in line:
+        break
+
+    self.format = CMakeFormat()
+    self.format.setIndentation(len(stmt) - len(stmt.lstrip()))
+
+    # Extract the content
+    stmt = stmt[stmt.find('(') + 1 : stmt.find(')')]
+
+    self.multiline = "\n" in stmt
+
+    # Split into test name + target
+    items = CMakeTarget.COMMAND_SPLIT_PATTERN.split(stmt)
+    assert len(items) >= 2
+
+    self.target = items[0]
+    self.dependencies = items[1:]
+
+  def collectTests(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    global_ctx.tests[self.target] = True
+
+  def toStringSingleLine(self):
+    """
+    Returns
+    -------
+    String
+    """
+    return self.format.getIndentationString() + "add_test(" + \
+           " ".join([self.target] + self.dependencies) + ")"
+
+  def toStringMultipleLines(self):
+    """
+    Returns
+    -------
+    String
+    """
+    output = self.format.getIndentationString() + "add_test("
+    spaces = "\n" + " " * len(output)
+    output += spaces.join([self.target] + self.dependencies) + ")"
+    return output
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    item = self.toStringSingleLine()
+
+    # Use multi-line format if the single-line output is too long
+    # or the original command is in multi-line mode and is not mutated.
+    if (len(item) > MAX_LINE_WIDTH
+        or (self.multiline and not self.isMutated())):
+      item = self.toStringMultipleLines()
+
+    output.append(item + "\n")
+
+
+class CMakeTarget(CMakeNode):
+  """A cmake add_library() or add_executable() command
+
+  Attributes
+  ----------
+  format : CMakeFormat
+  type : String
+  target : String
+  name : String
+  files : List[String]
+  multiline: Bool
+  """
+
+  # Static member variables
+  COMMAND_SPLIT_PATTERN = re.compile("[ \t\r\n]+")
+  INCLUDE_SPLIT_PATTERN = re.compile("[/.]")
+
+  def __init__(self, stream):
+    """
+    Parameters
+    ----------
+    stream : LineStream
+    """
+    super(CMakeTarget, self).__init__()
+
+    if stream is None:
+      return
+
+    stmt = ""
+    while stream.hasNextLine():
+      line = stream.pop()
+      stmt += line
+
+      # NOTE(jianqiao): We assume that the original CMakeLists.txt file
+      # is relatively "well written".
+      if ")" in line:
+        break
+
+    self.format = CMakeFormat()
+    self.format.setIndentation(len(stmt) - len(stmt.lstrip()))
+
+    # Figure out whether the target type.
+    for type in ["add_library", "add_executable"]:
+      if type in stmt:
+        self.type = type
+        break
+    assert self.type is not None
+
+    # Extract the content
+    stmt = stmt[stmt.find('(') + 1 : stmt.find(')')]
+
+    self.multiline = "\n" in stmt
+
+    # Split into target + file list
+    items = CMakeTarget.COMMAND_SPLIT_PATTERN.split(stmt)
+
+    self.target = items[0]
+    self.files = items[1:]
+
+    self.name = None
+    self.inferTargetName()
+
+  @staticmethod
+  def CreateLibrary(path, name, group):
+    node = CMakeTarget(None)
+
+    node.format = CMakeFormat()
+    node.type = "add_library"
+    node.target = CMakeNode.GenerateDefaultTarget(path, name)
+    node.name = name
+    node.multiline = False
+
+    files = [(ext, name + ext) for ext in group.files]
+
+    if not any([ext.startswith(".c") for ext in group.files]):
+      depth = group.path.count("/")
+      empty_cpp = "/".join([".."] * depth) + "/" + DUMMY_CPP_FILE_NAME
+      files.append((".cpp", empty_cpp))
+
+    node.files = [it[1] for it in sorted(files, key = lambda s : s[0])]
+
+    return node
+
+  def inferTargetName(self):
+    """Get target name if this target contains only matched .hpp/.cpp files.
+    """
+    target_name = None
+
+    for filename in self.files:
+      if DUMMY_CPP_FILE_NAME in filename:
+        continue
+
+      if "/" in filename or "$" in filename:
+        return
+
+      name, _ = os.path.splitext(filename)
+
+      if target_name is None:
+        target_name = name
+      elif target_name != name:
+        return
+
+    if target_name is None:
+      return
+
+    self.name = target_name
+
+  def isTest(self):
+    # Hardcoded rule that identifies extra test files.
+    return "test" in self.target
+
+  def isRegular(self, path):
+    if self.isTest():
+      return False
+
+    if self.target == CMakeNode.GenerateDefaultTarget(path):
+      return False
+
+    return True
+
+  def collectTests(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    if self.isTest():
+      global_ctx.tests[self.target] = True
+
+  def autofixTargets(self, local_ctx):
+    """
+    Parameters
+    ----------
+    local_ctx : CMakeLocalContext
+    """
+    name = self.name
+
+    # TODO(quickstep-team): Handle the non-regular cases.
+    if name is None:
+      return
+
+    if self.target in IGNORED_TARGETS:
+      return
+
+    if name not in local_ctx.sources:
+      self.setMutated(True)
+      self.files = []
+      return
+
+    group = local_ctx.sources[name]
+    files = [(ext, name + ext) for ext in group.files]
+
+    if not any([ext.startswith(".c") for ext in group.files]):
+      depth = group.path.count("/")
+      empty_cpp = "/".join([".."] * depth) + "/" + DUMMY_CPP_FILE_NAME
+      files.append((".cpp", empty_cpp))
+
+    files = [it[1] for it in sorted(files, key = lambda s : s[0])]
+
+    if self.files != files:
+      self.setMutated(True)
+      self.files = files
+
+    # Auto-fix target name.
+    if ((FORCE_DEFAULT_NAMING_RULE_FOR_LIBRARY and self.type == "add_library")
+        or (FORCE_DEFAULT_NAMING_RULE_FOR_EXECUTABLE and self.type == "add_executable")):
+      target = CMakeNode.GenerateDefaultTarget(group.path, name)
+
+      if target.startswith(self.target):
+        # Likely a module-all-in-one target.
+        pass
+      elif self.target != target:
+        self.setMutated(True)
+        self.target = target
+
+  def collectReferencedFiles(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    for filename in self.files:
+      output.append(filename)
+
+  def buildTargetIndex(self, path, output):
+    """
+    Parameters
+    ----------
+    path : String
+    output : Dict[String, String]
+    """
+    for filename in self.files:
+      filepath = path + "/" + filename
+
+      # Prefer regular targets.
+      if (filepath in output and self.name is None):
+        continue
+
+      output[filepath] = self.target
+
+  def collectTargets(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    output.append(self)
+
+  def collectDependencies(self, path, global_ctx, conditions):
+    """
+    Parameters
+    ----------
+    path : String
+    global_ctx : CMakeGlobalContext
+    conditions : List[String]
+    """
+    dependencies = []
+
+    for filename in self.files:
+      # Remove double quotes.
+      if filename.startswith("\""):
+        filename = filename[1:-1]
+
+      # Resolve indirect file name.
+      if filename.startswith("$"):
+        source_fp = ResolveIndirectFilename(path, filename)
+        if source_fp is None:
+          continue
+      else:
+        source_fp = path + "/" + filename
+
+      if source_fp not in global_ctx.file_index:
+        continue
+
+      # Get source file.
+      source = global_ctx.file_index[source_fp]
+
+      for ic in source.includes:
+        if ic.isConditional():
+          continue
+
+        item = ic.item
+
+        # Check if it is third-party include.
+        if item in THIRD_PARTY_LIBRARIES:
+          dependencies += THIRD_PARTY_LIBRARIES[item]
+          continue
+
+        label = CMakeTarget.INCLUDE_SPLIT_PATTERN.split(item)[0]
+        if label in THIRD_PARTY_LIBRARIES:
+          dependencies += THIRD_PARTY_LIBRARIES[label]
+          continue
+
+        # Check if it is a protobuf header.
+        if item.endswith(".pb.h"):
+          proto = CMakeNode.GenerateDefaultTarget("./" + item[:-5], "proto")
+          dependencies.append(proto)
+          continue
+
+        # Get file path.
+        filepath = "./" + item
+
+        if filepath not in global_ctx.target_index:
+          continue
+
+        dependencies.append(global_ctx.target_index[filepath])
+
+    global_ctx.dependencies[self.target] = \
+        [(dependency, conditions) for dependency in dependencies]
+
+  def toStringSingleLine(self):
+    """
+    Returns
+    -------
+    String
+    """
+    return self.format.getIndentationString() + self.type + "(" + \
+           " ".join([self.target] + self.files) + ")"
+
+  def toStringMultipleLines(self):
+    """
+    Returns
+    -------
+    String
+    """
+    output = self.format.getIndentationString() + self.type + "("
+    spaces = "\n" + " " * len(output)
+    output += spaces.join([self.target] + self.files) + ")"
+    return output
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    if len(self.files) == 0:
+      return
+
+    item = self.toStringSingleLine()
+
+    # Use multi-line format if the single-line output is too long
+    # or the original command is in multi-line mode and is not mutated.
+    if (len(item) > MAX_LINE_WIDTH
+        or (self.multiline and not self.isMutated())):
+      item = self.toStringMultipleLines()
+
+    output.append(item + "\n")
+
+
+class CMakeLink(CMakeNode):
+  """A cmake target_link_libraries() command
+
+  Attributes
+  ----------
+  format : CMakeFormat
+  target : String
+  dependencies : List[String]
+  multiline : Bool
+  """
+
+  # Static member variables
+  COMMAND_SPLIT_PATTERN = re.compile("[ \t\r\n]+")
+
+  def __init__(self, stream):
+    """
+    Parameters
+    ----------
+    stream : LineStream
+    """
+    super(CMakeLink, self).__init__()
+
+    if stream is None:
+      return
+
+    stmt = ""
+    while stream.hasNextLine():
+      line = stream.pop()
+      stmt += line
+
+      # NOTE(jianqiao): We assume that the original CMakeLists.txt file
+      # is relatively "well written".
+      if ")" in line:
+        break
+
+    self.format = CMakeFormat()
+    self.format.setIndentation(len(stmt) - len(stmt.lstrip()))
+
+    # Extract the content
+    stmt = stmt[stmt.find('(') + 1 : stmt.find(')')]
+
+    self.multiline = "\n" in stmt
+
+    # Split into target + file list
+    items = CMakeLink.COMMAND_SPLIT_PATTERN.split(stmt)
+
+    self.target = items[0]
+    self.dependencies = items[1:]
+
+    self.sortDependencies()
+
+  def isTest(self):
+    # Hardcoded rule that identifies extra test targets.
+    return "test" in self.target
+
+  def isRegular(self, path):
+    if self.isTest():
+      return False
+
+    if self.target == CMakeNode.GenerateDefaultTarget(path):
+      return False
+
+    return True
+
+  def collectLinks(self, output):
+    """
+    Parameters
+    ----------
+    output : List[CMakeLink]
+    """
+    output.append(self)
+
+  def collectDependencies(self, path, global_ctx, conditions):
+    """
+    Parameters
+    ----------
+    path : String
+    global_ctx : CMakeGlobalContext
+    conditions : List[String]
+    """
+    global_ctx.prior_dependencies[self.target] += \
+        [(dependency, conditions) for dependency in self.dependencies]
+
+  def sortDependencies(self):
+    self.dependencies = sorted(set(self.dependencies))
+
+  def autofixDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    target = self.target
+
+    # TODO(quickstep-team): Handle tests.
+    if target in global_ctx.tests:
+      return
+
+    # TODO(quickstep-team): Handle protobuf files.
+    if target.endswith("proto"):
+      return
+
+    if target in IGNORED_TARGETS:
+      return
+
+    self.setMutated(True)
+    self.dependencies = [x for x in self.dependencies if x in IGNORED_TARGETS]
+
+    if target in global_ctx.modules:
+      normal_dependencies = {}
+      conditional_dependencies = {}
+
+      if target in global_ctx.prior_dependencies:
+        for it in global_ctx.prior_dependencies[target]:
+          if len(it[1]) == 0:
+            normal_dependencies[it[0]] = True
+          else:
+            conditional_dependencies[it[0]] = True
+
+      for submodule in global_ctx.modules[target]:
+        if (submodule == target or submodule in global_ctx.tests):
+          continue
+
+        if (submodule not in global_ctx.dependencies
+            and submodule not in global_ctx.prior_dependencies):
+          continue
+
+        if (submodule in normal_dependencies
+            or submodule not in conditional_dependencies):
+          self.dependencies.append(submodule)
+
+      self.sortDependencies()
+      return
+
+    if target not in global_ctx.dependencies:
+      self.dependencies = []
+      return
+
+    # TODO(quickstep-team): Handle dependencies with conditions.
+    self.dependencies += [it[0] for it in global_ctx.dependencies[target]
+                                if it[0] != target and len(it[1]) == 0]
+    self.sortDependencies()
+
+  def toStringSingleLine(self):
+    """
+    Returns
+    -------
+    String
+    """
+    return self.format.getIndentationString() + "target_link_libraries(" + \
+           " ".join([self.target] + self.dependencies) + ")"
+
+  def toStringMultipleLines(self):
+    """
+    Returns
+    -------
+    String
+    """
+    output = self.format.getIndentationString() + "target_link_libraries("
+    spaces = "\n" + " " * len(output)
+    output += spaces.join([self.target] + self.dependencies) + ")"
+    return output
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    if len(self.dependencies) == 0:
+      return
+
+    item = None
+    if not self.multiline:
+      item = self.toStringSingleLine()
+
+    if item is None or len(item) > MAX_LINE_WIDTH:
+      item = self.toStringMultipleLines()
+
+    output.append(item + "\n")
+
+
+class CMakeGroup(CMakeNode):
+  """A group of cmake commands
+
+  Attributes
+  ----------
+  nodes : List[CMakeNode]
+  """
+
+  def __init__(self, stream):
+    """
+    Parameters
+    ----------
+    stream : LineStream
+    """
+    super(CMakeGroup, self).__init__()
+
+    self.nodes = []
+
+    if stream is None:
+      return
+
+    while stream.hasNextLine():
+      line = stream.top().lstrip()
+
+      if line.startswith("add_library"):
+        self.nodes.append(CMakeTarget(stream))
+        continue
+
+      if line.startswith("add_executable"):
+        self.nodes.append(CMakeTarget(stream))
+        continue
+
+      if line.startswith("add_subdirectory"):
+        self.nodes.append(CMakeSubdirectory(stream))
+        continue
+
+      if line.startswith("add_test"):
+        self.nodes.append(CMakeTest(stream))
+        continue
+
+      if line.startswith("if"):
+        self.nodes.append(CMakeIf(stream))
+        continue
+
+      if line.startswith("target_link_libraries"):
+        self.nodes.append(CMakeLink(stream))
+        continue
+
+      self.nodes.append(CMakeLine(stream))
+
+  def getChildren(self):
+    """
+    Returns
+    -------
+    List[CMakeNode]
+    """
+    return self.nodes
+
+  def writeToStream(self, output):
+    """
+    Parameters
+    ----------
+    output : List[String]
+    """
+    for node in self.nodes:
+      node.writeToStream(output)
+
+
+################################################################################
+############################# CMakeList Abstraction ############################
+################################################################################
+
+class CMakeLists:
+  """A CMakeLists.txt file
+
+  Attributes
+  ----------
+  path : String
+  root : CMakeGroup
+  newfile :  Bool
+  """
+
+  def __init__(self, path):
+    """
+    Parameters
+    ----------
+    path : String
+    """
+    self.path = path
+
+    if "CMakeLists.txt" in os.listdir(path):
+      with open(self.path + "/CMakeLists.txt", "rb") as file:
+        self.root = CMakeGroup(LineStream(file.readlines()))
+        self.newfile = False
+    else:
+      self.root = CMakeGroup(None)
+      self.root.nodes.append(CMakeApacheLicense())
+      self.root.nodes.append(CMakeLine.CreateEmptyLine())
+      self.newfile = True
+
+  def autofixTargets(self, ctx):
+    """
+    Parameters
+    ----------
+    ctx : CMakeLocalContext
+    """
+    self.root.autofixTargets(ctx)
+
+  def collectReferences(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    # Collect all referenced files.
+    unresolved_files = []
+    self.root.collectReferencedFiles(unresolved_files)
+
+    # Resolve file path.
+    referenced_files = {}
+    for filename in unresolved_files:
+      if DUMMY_CPP_FILE_NAME in filename:
+        continue
+
+      if filename.startswith("\""):
+        filename = filename[1:-1]
+
+      if filename.startswith("$"):
+        filepath = ResolveIndirectFilename(self.path, filename)
+        if filepath is not None:
+          referenced_files[filepath] = True
+        continue
+
+      filepath = filename
+      if not filename.startswith("/"):
+        filepath = self.path + "/" + filename
+
+      referenced_files[filepath] = True
+
+    # Register all references into global context.
+    for filepath in referenced_files:
+      global_ctx.addFileReference(filepath)
+
+  def addMissingTargets(self, local_ctx, global_ctx):
+    """
+    Parameters
+    ----------
+    ctx : CMakeLocalContext
+    """
+    targets = {}
+
+    # Collect all existing targets.
+    for node in self.root.nodes:
+      if isinstance(node, CMakeTarget):
+        targets[node.target] = node
+
+    # Get unreferenced file groups.
+    unreferenced_names = []
+
+    for name in local_ctx.sources:
+      group = local_ctx.sources[name]
+
+      for filepath in group.getFilePaths():
+        if filepath not in global_ctx.referenced_files:
+          unreferenced_names.append(name)
+          break
+
+    # Add targets for unferenced file groups.
+    for name in unreferenced_names:
+      node = CMakeTarget.CreateLibrary(self.path, name, local_ctx.sources[name])
+
+      self.root.nodes.append(CMakeLine.CreateEmptyLine())
+      self.root.nodes.append(node)
+
+  def buildTargetIndex(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.root.buildTargetIndex(self.path, global_ctx.target_index)
+
+  def collectDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.root.collectDependencies(self.path, global_ctx, [])
+
+  def collectTests(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.root.collectTests(global_ctx)
+
+  def addMissingDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    # First add missing target link entries.
+    links = []
+    self.root.collectLinks(links)
+    links = set([node.target for node in links])
+
+    targets = []
+    self.root.collectTargets(targets)
+
+    for e in targets:
+      if (not e.isTest()) and e.target not in links:
+        node = CMakeLink(None)
+        node.format = CMakeFormat()
+        node.target = e.target
+        node.dependencies = []
+        node.multiline = True
+
+        self.root.nodes.append(CMakeLine.CreateEmptyLine())
+        self.root.nodes.append(node)
+
+    # Then register non-test targets into the current module.
+    module = CMakeNode.GenerateDefaultTarget(self.path)
+
+    global_ctx.modules[module] += [node.target for node in targets
+                                               if not node.isTest()]
+
+    # Then register the current module into upper level module.
+    if "test" not in module and not self.isEmpty():
+      global_ctx.modules[module[:module.rfind("_")]].append(module)
+
+  def addMissingSubdirectories(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    if self.isEmpty():
+      return
+
+    nodes = []
+    self.root.collectSubdirectories(nodes)
+
+    subdirectories = {}
+    for node in nodes:
+      name = node.name
+
+      if name.startswith("\""):
+        name = name[1:-1]
+
+      # Resolve indirect name.
+      if name.startswith("$"):
+        name = ResolveIndirectFilename(self.path, name)
+        if name is None:
+          continue
+
+      if name in subdirectories:
+        print "Warning: Possibly duplicated add_subdirectory(" + \
+              name + ") in", self.path
+
+      subdirectories[name] = node
+
+    residuals = []
+
+    for subdir in global_ctx.subdirectories:
+      if subdir.startswith(self.path):
+        name = subdir[len(self.path)+1:]
+        if name not in subdirectories:
+          self.root.nodes.append(CMakeLine.CreateEmptyLine())
+          self.root.nodes.append(CMakeSubdirectory.CreateDirectory(name))
+        else:
+          del subdirectories[name]
+      else:
+        residuals.append(subdir)
+
+    for node in subdirectories.values():
+      node.name = None
+
+    global_ctx.subdirectories = residuals + [self.path]
+
+  def autofixDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.root.autofixDependencies(global_ctx)
+
+  def layoutTargets(self):
+    nodes = self.root.nodes
+    num_nodes = len(nodes)
+
+    anchor_pos = -1
+    for i in range(num_nodes):
+      node = nodes[i]
+      if isinstance(node, CMakeTarget):
+        anchor_pos = i
+        break
+
+    if anchor_pos < 0:
+      return
+
+    targets = []
+    suffix = []
+    for i in range(anchor_pos, num_nodes):
+      node = nodes[i]
+      if (isinstance(nodes[i], CMakeTarget)
+          and node.isRegular(self.path)
+          and node.target not in ANCHORED_TARGETS):
+        targets.append((node.target, node))
+      else:
+        suffix.append(node)
+
+    output = [nodes[i] for i in range(anchor_pos)]
+    output += [it[1] for it in sorted(targets, key = lambda s : s[0])]
+    output += suffix
+
+    self.root.nodes = output
+
+  def layoutSubdirectories(self):
+    nodes = self.root.nodes
+    num_nodes = len(nodes)
+
+    anchor_pos = -1
+    for i in range(num_nodes):
+      node = nodes[i]
+      if isinstance(node, CMakeSubdirectory) and node.isRegular():
+        anchor_pos = i
+        break
+
+    if anchor_pos < 0:
+      return
+
+    subdirectories = []
+    suffix = []
+    for i in range(anchor_pos, num_nodes):
+      node = nodes[i]
+      if isinstance(nodes[i], CMakeSubdirectory) and node.isRegular():
+        subdirectories.append((node.name, node))
+      else:
+        suffix.append(node)
+
+    output = [nodes[i] for i in range(anchor_pos)]
+    output += [it[1] for it in sorted(subdirectories, key = lambda s : s[0])]
+    output += suffix
+
+    self.root.nodes = output
+
+  def layoutLinkDependencies(self):
+    nodes = self.root.nodes
+    num_nodes = len(nodes)
+
+    anchor_pos = -1
+    for i in range(num_nodes):
+      node = nodes[i]
+      if isinstance(node, CMakeLink):
+        anchor_pos = i
+        break
+
+    if anchor_pos < 0:
+      return
+
+    links = []
+    suffix = []
+    for i in range(anchor_pos, num_nodes):
+      node = nodes[i]
+      if (isinstance(nodes[i], CMakeLink)
+          and node.isRegular(self.path)
+          and node.target not in ANCHORED_LINKS):
+        links.append((node.target, node))
+      else:
+        suffix.append(node)
+
+    output = [nodes[i] for i in range(anchor_pos)]
+    output += [it[1] for it in sorted(links, key = lambda s : s[0])]
+    output += suffix
+
+    self.root.nodes = output
+
+  def collapseEmptyLines(self):
+    output = []
+    state = False
+    for node in self.root.nodes:
+      if (not isinstance(node, CMakeLine)
+          or len(node.line) != 0):
+        output.append(node)
+        state = False
+        continue
+
+      if not state:
+        output.append(node)
+
+      state = True
+
+    self.root.nodes = output
+
+  def layout(self):
+    """Rearrange the commands.
+    """
+    self.layoutSubdirectories()
+    self.layoutTargets()
+    self.layoutLinkDependencies()
+    self.collapseEmptyLines()
+
+  def isEmpty(self):
+    return self.newfile and len(self.root.nodes) <= 2
+
+  def writeToFile(self):
+    if self.isEmpty():
+      return
+
+    output = []
+    self.root.writeToStream(output)
+
+    while len(output) > 0:
+      line = output.pop()
+      if len(line.strip()) != 0:
+        output.append(line)
+        break
+
+    if len(output) == 0:
+      return
+
+    with open(self.path + "/CMakeLists.txt", "wb") as file:
+      file.write("".join(output))
+      file.close()
+
+
+################################################################################
+########################## Source Code File Information ########################
+################################################################################
+
+class IncludeItem:
+  """A #include item
+
+  Attributes
+  ----------
+  item : String
+  dependencies : List[String]
+  """
+
+  # Static member variables
+  INCLUDE_PATTERN = re.compile("#include [<\"](.*)[>\"]")
+  DEPENDENCY_PATTERN = re.compile("#ifdef *([^ ]*)")
+
+  def __init__(self, text, dependencies):
+    """Constructor
+    """
+    match = IncludeItem.INCLUDE_PATTERN.search(text)
+    self.item = match.group(1)
+
+    self.dependencies = []
+
+    # Currently we consider only #ifdef dependencies
+    for line in dependencies:
+      if not line.startswith("#ifdef"):
+        continue
+
+      match = IncludeItem.DEPENDENCY_PATTERN.search(line)
+      self.dependencies.append(match.group(1))
+
+  def isConditional(self):
+    return len(self.dependencies) != 0
+
+
+class SourceFile:
+  """A source code file
+
+  Attributes
+  ----------
+  path : String
+  name : String
+  includes : List[String]
+  """
+
+  def __init__(self, path, name):
+    """
+    Parameters
+    ----------
+    path : String
+    name : String
+    """
+    self.path = path
+    self.name = name
+    self.includes = []
+
+    # Parse include items
+    with open(self.path + "/" + self.name, "rb") as file:
+      dependencies = []
+
+      for line in file:
+        # Remove white spaces
+        line = line.strip()
+
+        # Record dependencies
+        if line.startswith("#if"):
+          dependencies.append(line)
+
+        if line.startswith("#endif"):
+          dependencies.pop()
+
+        # Add an include item
+        if line.startswith("#include"):
+          self.includes.append(IncludeItem(line, dependencies))
+
+
+class SourceGroup:
+  """hpp and/or cpp file(s) that form a library or executable
+
+  Attributes
+  ----------
+  path : String
+  name : String
+  files : Dict[String, SourceFile]
+  """
+
+  def __init__(self, path, name):
+    """
+    Parameters
+    ----------
+    path : String
+    name : String
+    """
+    self.path = path
+    self.name = name
+    self.files = {}
+
+  def addExtension(self, ext):
+    """Add a file with the specified extension (e.g. .hpp, .cpp) into this group
+
+    Parameters
+    ----------
+    ext : String
+    """
+    self.files[ext] = SourceFile(self.path, self.name + ext)
+
+  def getFileNames(self):
+    return [self.name + ext for ext in self.files]
+
+  def getFilePaths(self):
+    return [self.path + "/" + self.name + ext for ext in self.files]
+
+
+################################################################################
+######################### Directory / Top-level Wrapper ########################
+################################################################################
+
+class Directory:
+  """Directory node in the document tree
+
+  Attributes
+  ----------
+  path : String
+  directories : Dict[String, Directory]
+  sources : Dict[String, SourceGroup]
+  """
+
+  def __init__(self, path):
+    """
+    Parameters
+    ----------
+    path : String
+    """
+    self.path = path
+    self.directories = {}
+    self.sources = {}
+
+    # Parse directories.
+    for filename in os.listdir(path):
+      fullpath = path + "/" + filename
+      if (not os.path.isdir(fullpath)
+          or filename.startswith(".")
+          or fullpath in EXCLUDED_DIRECTORIES):
+        continue
+
+      self.directories[filename] = Directory(fullpath)
+
+    # Parse source code files.
+    for filename in os.listdir(path):
+      fullpath = path + "/" + filename
+
+      if (os.path.isdir(fullpath)
+          or filename.startswith(".")
+          or fullpath in EXCLUDED_FILES):
+        continue
+
+      name, ext = os.path.splitext(filename)
+
+      if ext not in SOURCE_CODE_FILE_EXTENSIONS:
+        continue
+
+      self.addSourceCodeFile(name, ext)
+
+    # Parse CMakeLists.txt file.
+    self.cmakelists = CMakeLists(path)
+
+  def addSourceCodeFile(self, name, ext):
+    """Parse and add a source code file
+
+    Parameters
+    ----------
+    path : String
+    ext : String
+    """
+    if name not in self.sources:
+      self.sources[name] = SourceGroup(self.path, name)
+
+    self.sources[name].addExtension(ext)
+
+  def getChildren(self):
+    """Get child directories.
+
+    Returns
+    ----------
+    Dict[String, Directory]
+    """
+    return self.directories.values()
+
+  @TopDownVisitor
+  def autofixTargets(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.autofixTargets(CMakeLocalContext(self.sources))
+
+  @TopDownVisitor
+  def collectReferences(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.collectReferences(global_ctx)
+
+  @TopDownVisitor
+  def addMissingTargets(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.addMissingTargets(CMakeLocalContext(self.sources), global_ctx)
+
+  @TopDownVisitor
+  def buildFileIndex(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    for name in self.sources:
+      group = self.sources[name]
+      for ext in group.files:
+        filepath = self.path + "/" + name + ext
+        global_ctx.file_index[filepath] = group.files[ext]
+
+  @TopDownVisitor
+  def buildTargetIndex(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.buildTargetIndex(global_ctx)
+
+  @TopDownVisitor
+  def collectDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.collectDependencies(global_ctx)
+
+  @TopDownVisitor
+  def collectTests(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.collectTests(global_ctx)
+
+  @TopDownVisitor
+  def addMissingDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.addMissingDependencies(global_ctx)
+
+  @TopDownVisitor
+  def autofixDependencies(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.autofixDependencies(global_ctx)
+
+  @BottomUpVisitor
+  def addMissingSubdirectories(self, global_ctx):
+    """
+    Parameters
+    ----------
+    global_ctx : CMakeGlobalContext
+    """
+    self.cmakelists.addMissingSubdirectories(global_ctx)
+
+  @TopDownVisitor
+  def layoutCMakeLists(self):
+    """Rearrange the commands in cmakelists.
+    """
+    self.cmakelists.layout()
+
+  def writeCMakeLists(self):
+    # Recursively process subdirectories.
+    for name in self.directories:
+      self.directories[name].writeCMakeLists()
+
+    self.cmakelists.writeToFile()
+
+
+class DocumentRoot:
+  """The document tree root
+
+  Attributes
+  ----------
+  path : String
+  directories : Dict[String, Directory]
+  """
+
+  def __init__(self):
+    """Constructor
+    """
+    self.root = Directory(".")
+
+  def autofixCMakeLists(self):
+    # Create a global context for storing intermediate information.
+    global_ctx = CMakeGlobalContext(self)
+
+    # Fix existing targets.
+    self.root.autofixTargets(global_ctx)
+
+    # Collect referenced files.
+    self.root.collectReferences(global_ctx)
+
+    # Add missing targets.
+    self.root.addMissingTargets(global_ctx)
+
+    # Collect dependencies.
+    self.root.buildFileIndex(global_ctx)
+    self.root.buildTargetIndex(global_ctx)
+    self.root.collectDependencies(global_ctx)
+
+    # Collect tests.
+    self.root.collectTests(global_ctx)
+
+    # Add missing link dependencies.
+    self.root.addMissingDependencies(global_ctx)
+
+    # Fix link dependencies.
+    self.root.autofixDependencies(global_ctx)
+
+    # Add missing subdirectories.
+    self.root.addMissingSubdirectories(global_ctx)
+
+    # Rearrange the order of cmake commands.
+    self.root.layoutCMakeLists()
+
+  def writeCMakeLists(self):
+    self.root.writeCMakeLists()
+
+
+################################################################################
+################################# Entry Point ##################################
+################################################################################
+
+if __name__ == "__main__":
+  document = DocumentRoot()
+  document.autofixCMakeLists()
+  document.writeCMakeLists()