aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.4/python-lib/cuddlefish/docs/generate.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.4/python-lib/cuddlefish/docs/generate.py')
-rw-r--r--tools/addon-sdk-1.4/python-lib/cuddlefish/docs/generate.py230
1 files changed, 230 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.4/python-lib/cuddlefish/docs/generate.py b/tools/addon-sdk-1.4/python-lib/cuddlefish/docs/generate.py
new file mode 100644
index 0000000..1c0fbf9
--- /dev/null
+++ b/tools/addon-sdk-1.4/python-lib/cuddlefish/docs/generate.py
@@ -0,0 +1,230 @@
+import os
+import sys
+import shutil
+import hashlib
+import tarfile
+import StringIO
+
+from cuddlefish import packaging
+from cuddlefish import Bunch
+from cuddlefish.docs import apiparser
+from cuddlefish.docs import apirenderer
+from cuddlefish.docs import webdocs
+import simplejson as json
+
+DOCS_DIR = "doc"
+DIGEST = "status.md5"
+TGZ_FILENAME = "addon-sdk-docs.tgz"
+
+def clean_generated_docs(docs_dir):
+ status_file = os.path.join(docs_dir, "status.md5")
+ if os.path.exists(status_file):
+ os.remove(status_file)
+ index_file = os.path.join(docs_dir, "index.html")
+ if os.path.exists(index_file):
+ os.remove(index_file)
+ dev_guide_dir = os.path.join(docs_dir, "dev-guide")
+ if os.path.exists(dev_guide_dir):
+ shutil.rmtree(dev_guide_dir)
+ api_doc_dir = os.path.join(docs_dir, "packages")
+ if os.path.exists(api_doc_dir):
+ shutil.rmtree(api_doc_dir)
+
+def generate_static_docs(env_root, base_url=None):
+ docs_dir = os.path.join(env_root, DOCS_DIR)
+ clean_generated_docs(docs_dir)
+ generate_docs(env_root, base_url=base_url, stdout=StringIO.StringIO())
+ tgz = tarfile.open(TGZ_FILENAME, 'w:gz')
+ tgz.add(docs_dir, DOCS_DIR)
+ tgz.close()
+ return TGZ_FILENAME
+
+def generate_docs(env_root, base_url=None, filename=None, stdout=sys.stdout):
+ docs_dir = os.path.join(env_root, DOCS_DIR)
+ base_url = calculate_base_url(base_url, docs_dir)
+ # if we were given a filename, just generate the named file
+ # and return its URL
+ if filename:
+ return generate_named_file(env_root, base_url, filename)
+ # if the generated docs don't exist, generate everything
+ if not os.path.exists(os.path.join(docs_dir, "index.html")):
+ print >>stdout, "Generating documentation..."
+ generate_docs_from_scratch(env_root, base_url, docs_dir)
+ current_status = calculate_current_status(env_root)
+ open(os.path.join(env_root, DOCS_DIR, DIGEST), "w").write(current_status)
+ else:
+ current_status = calculate_current_status(env_root)
+ previous_status_file = os.path.join(env_root, DOCS_DIR, DIGEST)
+ docs_are_up_to_date = False
+ if os.path.exists(previous_status_file):
+ docs_are_up_to_date = current_status == open(previous_status_file, "r").read()
+ # if the docs are not up to date, generate everything
+ if not docs_are_up_to_date:
+ print >>stdout, "Regenerating documentation..."
+ generate_docs_from_scratch(env_root, base_url, docs_dir)
+ open(os.path.join(env_root, DOCS_DIR, DIGEST), "w").write(current_status)
+ return base_url + "index.html"
+
+def calculate_base_url(base_url, docs_dir):
+ if base_url == None:
+ base_url_path = docs_dir
+ # this is to ensure the path starts with "/"
+ # whether or not it's on Windows
+ # there might be a better way
+ if not docs_dir.startswith("/"):
+ base_url_path = "/" + base_url_path
+ base_url_path_pieces = base_url_path.split(os.sep)
+ base_url = "file://" + "/".join(base_url_path_pieces) + "/"
+ return base_url
+
+def generate_named_file(env_root, base_url, filename):
+ docs_dir = os.path.join(env_root, DOCS_DIR)
+ web_docs = webdocs.WebDocs(env_root, base_url)
+
+ # next, generate api doc or guide doc
+ abs_path = os.path.abspath(filename)
+ if abs_path.startswith(os.path.join(env_root, 'packages')):
+ return generate_api_doc(env_root, abs_path, web_docs)
+ elif abs_path.startswith(os.path.join(env_root, DOCS_DIR, 'dev-guide-source')):
+ return generate_guide_doc(env_root, abs_path, web_docs)
+ else:
+ raise ValueError("Not a valid path to a documentation file")
+
+# this function builds a hash of the name and last modification date of:
+# * every file in "packages" which ends in ".md"
+# * every file in "static-files" which does not start with "."
+def calculate_current_status(env_root):
+ current_status = hashlib.md5()
+ package_src_dir = os.path.join(env_root, "packages")
+ for (dirpath, dirnames, filenames) in os.walk(package_src_dir):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ current_status.update(filename)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename))))
+ guide_src_dir = os.path.join(env_root, DOCS_DIR, "dev-guide-source")
+ for (dirpath, dirnames, filenames) in os.walk(guide_src_dir):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ current_status.update(filename)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename))))
+ base_html_file = os.path.join(env_root, DOCS_DIR, "static-files", "base.html")
+ current_status.update(base_html_file)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, base_html_file))))
+ return current_status.digest()
+
+def generate_docs_from_scratch(env_root, base_url, docs_dir):
+ web_docs = webdocs.WebDocs(env_root, base_url)
+ clean_generated_docs(docs_dir)
+
+ # py2.5 doesn't have ignore=, so we delete tempfiles afterwards. If we
+ # required >=py2.6, we could use ignore=shutil.ignore_patterns("*~")
+ for (dirpath, dirnames, filenames) in os.walk(docs_dir):
+ for n in filenames:
+ if n.endswith("~"):
+ os.unlink(os.path.join(dirpath, n))
+
+ # generate api docs from all packages
+ os.mkdir(os.path.join(docs_dir, "packages"))
+ # create the index file and save that
+ pkg_cfg = packaging.build_pkg_cfg(env_root)
+ index = json.dumps(packaging.build_pkg_index(pkg_cfg))
+ index_path = os.path.join(docs_dir, "packages", 'index.json')
+ open(index_path, 'w').write(index)
+
+ # for each package, generate its docs
+ for pkg_name, pkg in pkg_cfg['packages'].items():
+ src_dir = pkg.root_dir
+ package_dirname = os.path.basename(src_dir)
+ dest_dir = os.path.join(docs_dir, "packages", package_dirname)
+ os.mkdir(dest_dir)
+
+ src_readme = os.path.join(src_dir, "README.md")
+ if os.path.exists(src_readme):
+ shutil.copyfile(src_readme,
+ os.path.join(dest_dir, "README.md"))
+
+ # create the package page
+ package_filename = os.path.join(dest_dir, pkg_name + ".html")
+ if not os.path.exists(package_filename):
+ package_doc_html = web_docs.create_package_page(pkg_name)
+ open(package_filename, "w").write(package_doc_html)
+
+ # generate all the API docs
+ docs_src_dir = os.path.join(src_dir, "doc")
+ if os.path.isdir(os.path.join(src_dir, "docs")):
+ docs_src_dir = os.path.join(src_dir, "docs")
+ generate_file_tree(env_root, docs_src_dir, web_docs, generate_api_doc)
+
+ # generate all the guide docs
+ dev_guide_src = os.path.join(env_root, DOCS_DIR, "dev-guide-source")
+ generate_file_tree(env_root, dev_guide_src, web_docs, generate_guide_doc)
+
+ # make /md/dev-guide/welcome.html the top level index file
+ shutil.copy(os.path.join(env_root, DOCS_DIR, 'dev-guide', 'welcome.html'), \
+ os.path.join(docs_dir, 'index.html'))
+
+def generate_file_tree(env_root, src_dir, web_docs, generate_file):
+ for (dirpath, dirnames, filenames) in os.walk(src_dir):
+ assert dirpath.startswith(src_dir) # what is this for??
+ for filename in filenames:
+ if filename.endswith("~"):
+ continue
+ src_path = os.path.join(dirpath, filename)
+ generate_file(env_root, src_path, web_docs)
+
+def generate_api_doc(env_root, src_dir, web_docs):
+ if src_dir.endswith(".md"):
+ dest_dir, filename = get_api_doc_dest_path(env_root, src_dir)
+ if not os.path.exists(dest_dir):
+ os.makedirs(dest_dir)
+
+ # parse and JSONify the API docs
+ docs_md = open(src_dir, 'r').read()
+ docs_parsed = list(apiparser.parse_hunks(docs_md))
+ docs_json = json.dumps(docs_parsed)
+ dest_path_json = os.path.join(dest_dir, filename) + ".json"
+ replace_file(dest_path_json, docs_json)
+
+ # write the HTML div files
+ docs_div = apirenderer.json_to_div(docs_parsed, src_dir)
+ dest_path_div = os.path.join(dest_dir, filename) + ".div"
+ replace_file(dest_path_div, docs_div)
+
+ # write the standalone HTML files
+ docs_html = web_docs.create_module_page(src_dir)
+ dest_path_html = os.path.join(dest_dir, filename) + ".html"
+ replace_file(dest_path_html, docs_html)
+
+ return dest_path_html
+
+def generate_guide_doc(env_root, src_dir, web_docs):
+ if src_dir.endswith(".md"):
+ dest_dir, filename = get_guide_doc_dest_path(env_root, src_dir)
+ if not os.path.exists(dest_dir):
+ os.makedirs(dest_dir)
+ # write the standalone HTML files
+ docs_html = web_docs.create_guide_page(src_dir)
+ dest_path_html = os.path.join(dest_dir, filename) + ".html"
+ replace_file(dest_path_html, docs_html)
+ return dest_path_html
+
+def replace_file(dest_path, file_contents):
+ if os.path.exists(dest_path):
+ os.remove(dest_path)
+ open(dest_path, "w").write(file_contents)
+
+# Given the full path to an API source file, and the root,
+# return a tuple of:
+# 1) the full path to the corresponding HTML file, without the filename
+# 2) the filename without the extension
+def get_guide_doc_dest_path(env_root, src_dir):
+ src_dir_relative = src_dir[len(os.path.join(env_root, DOCS_DIR, "dev-guide-source")) + 1:]
+ return os.path.split(os.path.join(env_root, DOCS_DIR, "dev-guide", src_dir_relative)[:-3])
+
+# Given the full path to a dev guide source file, and the root,
+# return a tuple of:
+# 1) the full path to the corresponding HTML file, without the filename
+# 2) the filename without the extension
+def get_api_doc_dest_path(env_root, src_dir):
+ src_dir_relative = src_dir[len(env_root) + 1:]
+ return os.path.split(os.path.join(env_root, DOCS_DIR, src_dir_relative)[:-3]) \ No newline at end of file