Use llvm::sys::Path to implement "mkdir -p".

Change-Id: I3bb36e1a8050bbd9744fd02935b6186d2f285015
diff --git a/Android.mk b/Android.mk
index be5ce9a..177ba91 100644
--- a/Android.mk
+++ b/Android.mk
@@ -132,6 +132,7 @@
 LOCAL_SRC_FILES :=	\
 	slang_driver.cpp	\
 	slang.cpp	\
+	slang_utils.cpp	\
 	slang_backend.cpp	\
 	slang_pragma_recorder.cpp	\
 	slang_diagnostic_buffer.cpp	\
diff --git a/slang.cpp b/slang.cpp
index cffe2cf..770d37b 100644
--- a/slang.cpp
+++ b/slang.cpp
@@ -4,6 +4,8 @@
 
 #include "llvm/Target/TargetSelect.h"
 
+#include "llvm/System/Path.h"
+
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -31,6 +33,7 @@
 
 #include "clang/Parse/ParseAST.h"
 
+#include "slang_utils.h"
 #include "slang_backend.h"
 
 using namespace slang;
@@ -251,42 +254,19 @@
   return true;
 }
 
-static void _mkdir_given_a_file(const char *file) {
-  char buf[256];
-  char *tmp, *p = NULL;
-  size_t len = strlen(file);
-
-  if (len + 1 <= sizeof(buf))
-    tmp = buf;
-  else
-    tmp = new char[len + 1];
-
-  strcpy(tmp, file);
-
-  if (tmp[len - 1] == '/')
-    tmp[len - 1] = 0;
-
-  for (p = tmp + 1; *p; p++) {
-    if (*p == '/') {
-      *p = 0;
-      mkdir(tmp, S_IRWXU);
-      *p = '/';
-    }
-  }
-
-  if (tmp != buf)
-    delete[] tmp;
-}
-
 bool Slang::setOutput(const char *OutputFile) {
+  llvm::sys::Path OutputFilePath(OutputFile);
   std::string Error;
 
   switch (mOT) {
     case OT_Dependency:
     case OT_Assembly:
     case OT_LLVMAssembly: {
-      _mkdir_given_a_file(OutputFile);
-      mOS.reset(new llvm::raw_fd_ostream(OutputFile, Error, 0));
+      if (!SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(),
+                                                  &Error))
+        mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
+            << Error;
+      mOS.reset(new llvm::tool_output_file(OutputFile, Error, 0));
       break;
     }
     case OT_Nothing: {
@@ -295,15 +275,18 @@
     }
     case OT_Object:
     case OT_Bitcode: {
-      _mkdir_given_a_file(OutputFile);
-      mOS.reset(new llvm::raw_fd_ostream(OutputFile,
-                                         Error,
-                                         llvm::raw_fd_ostream::F_Binary));
+      if (!SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(),
+                                                  &Error))
+        mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
+            << Error;
+      mOS.reset(new llvm::tool_output_file(OutputFile,
+                                           Error,
+                                           llvm::raw_fd_ostream::F_Binary));
       break;
     }
-    default:
+    default: {
       llvm_unreachable("Unknown compiler output type");
-      break;
+    }
   }
 
   if (!Error.empty()) {
@@ -319,8 +302,13 @@
 }
 
 bool Slang::setDepOutput(const char *OutputFile) {
+  llvm::sys::Path OutputFilePath(OutputFile);
   std::string Error;
-  _mkdir_given_a_file(OutputFile);
+
+  if (!SlangUtils::CreateDirectoryWithParents(OutputFilePath.getDirname(),
+                                              &Error))
+    mDiagnostics->Report(clang::diag::err_fe_error_opening) << OutputFile
+        << Error;
   mDOS.reset(new llvm::raw_fd_ostream(OutputFile, Error, 0));
 
   if (!Error.empty()) {
diff --git a/slang_diagnostic_buffer.h b/slang_diagnostic_buffer.h
index 3ed48be..b6f223e 100644
--- a/slang_diagnostic_buffer.h
+++ b/slang_diagnostic_buffer.h
@@ -1,5 +1,5 @@
-#ifndef _SLANG_DIAGNOSTIC_BUFFER_H
-#define _SLANG_DIAGNOSTIC_BUFFER_H
+#ifndef _SLANG_COMPILER_DIAGNOSTIC_BUFFER_H
+#define _SLANG_COMPILER_DIAGNOSTIC_BUFFER_H
 
 #include "llvm/Support/raw_ostream.h"
 
diff --git a/slang_rs_reflect_utils.cpp b/slang_rs_reflect_utils.cpp
index 75db21a..94fb8b0 100644
--- a/slang_rs_reflect_utils.cpp
+++ b/slang_rs_reflect_utils.cpp
@@ -1,11 +1,11 @@
 #include "slang_rs_reflect_utils.h"
 
-#include <cstdlib>
 #include <cstdio>
 #include <cstring>
 
-#include <sys/stat.h>
-#include <sys/types.h>
+#include "llvm/ADT/StringRef.h"
+
+#include "slang_utils.h"
 
 namespace slang {
 
@@ -85,36 +85,6 @@
     return InternalFileNameConvert(rsFileName, true);
 }
 
-bool RSSlangReflectUtils::mkdir_p(const char *path) {
-    char buf[256];
-    char *tmp, *p = NULL;
-    size_t len = strlen(path);
-
-    if (len + 1 <= sizeof(buf))
-        tmp = buf;
-    else
-        tmp = new char[len + 1];
-
-    strcpy(tmp, path);
-
-    if (tmp[len - 1] == '/')
-        tmp[len - 1] = 0;
-
-    for (p = tmp + 1; *p; p++) {
-        if (*p == '/') {
-            *p = 0;
-            mkdir(tmp, S_IRWXU);
-            *p = '/';
-        }
-    }
-    mkdir(tmp, S_IRWXU);
-
-    if (tmp != buf)
-        delete[] tmp;
-
-    return true;
-}
-
 static bool GenerateAccessorHeader(
     const RSSlangReflectUtils::BitCodeAccessorContext &context, FILE *pfout) {
     fprintf(pfout, "/*\n");
@@ -249,7 +219,8 @@
     const BitCodeAccessorContext &context) {
     string output_path = ComputePackagedPath(context.reflectPath,
                                              context.packageName);
-    if (!mkdir_p(output_path.c_str())) {
+    if (!SlangUtils::CreateDirectoryWithParents(llvm::StringRef(output_path),
+                                                NULL)) {
         fprintf(stderr, "Error: could not create dir %s\n",
                 output_path.c_str());
         return false;
diff --git a/slang_rs_reflect_utils.h b/slang_rs_reflect_utils.h
index d4124f2..d1b8328 100644
--- a/slang_rs_reflect_utils.h
+++ b/slang_rs_reflect_utils.h
@@ -59,10 +59,6 @@
   // rsFileName: the input .rs file name (with or without path).
   static std::string BCFileNameFromRSFileName(const char *rsFileName);
 
-  // "mkdir -p"
-  static bool mkdir_p(const char *path);
-
-
   // Generate the bit code accessor Java source file.
   static bool GenerateBitCodeAccessor(const BitCodeAccessorContext &context);
 };
diff --git a/slang_rs_reflection.cpp b/slang_rs_reflection.cpp
index d667058..8cdb78e 100644
--- a/slang_rs_reflection.cpp
+++ b/slang_rs_reflection.cpp
@@ -7,6 +7,7 @@
 
 #include "llvm/ADT/APFloat.h"
 
+#include "slang_utils.h"
 #include "slang_rs_context.h"
 #include "slang_rs_export_var.h"
 #include "slang_rs_export_func.h"
@@ -460,36 +461,10 @@
     return NULL;
 }
 
-bool RSReflection::openScriptFile(Context &C,
-                                  const std::string &ClassName,
-                                  std::string &ErrorMsg) {
-  if (!C.mUseStdout) {
-    C.mOF.clear();
-    std::string _path = RSSlangReflectUtils::ComputePackagedPath(
-        mRSContext->getReflectJavaPathName().c_str(),
-        C.getPackageName().c_str());
-
-    RSSlangReflectUtils::mkdir_p(_path.c_str());
-    C.mOF.open((_path + "/" + ClassName + ".java").c_str());
-    if (!C.mOF.good()) {
-      ErrorMsg = "failed to open file '" + _path + "/" + ClassName
-          + ".java' for write";
-
-      return false;
-    }
-  }
-  return true;
-}
-
 /********************** Methods to generate script class **********************/
 bool RSReflection::genScriptClass(Context &C,
                                   const std::string &ClassName,
                                   std::string &ErrorMsg) {
-  // Open the file
-  if (!openScriptFile(C, ClassName, ErrorMsg)) {
-    return false;
-  }
-
   if (!C.startClass(Context::AM_Public,
                     false,
                     ClassName,
@@ -1220,11 +1195,6 @@
                                 std::string &ErrorMsg) {
   std::string ClassName = RS_TYPE_CLASS_NAME_PREFIX + ERT->getName();
 
-  // Open the file
-  if (!openScriptFile(C, ClassName, ErrorMsg)) {
-    return false;
-  }
-
   if (!C.startClass(Context::AM_Public,
                     false,
                     ClassName,
@@ -1694,9 +1664,11 @@
   if ((OutputPackageName == NULL) ||
       (*OutputPackageName == '\0') ||
       strcmp(OutputPackageName, "-") == 0)
-    C = new Context(InputFileName, "<Package Name>", ResourceId, true);
+    C = new Context(mRSContext->getReflectJavaPathName(), InputFileName,
+                    "<Package Name>", ResourceId, true);
   else
-    C = new Context(InputFileName, OutputPackageName, ResourceId, false);
+    C = new Context(mRSContext->getReflectJavaPathName(), InputFileName,
+                    OutputPackageName, ResourceId, false);
 
   if (C != NULL) {
     std::string ErrorMsg, ScriptClassName;
@@ -1772,6 +1744,28 @@
   "android.util.Log",
 };
 
+bool RSReflection::Context::openClassFile(const std::string &ClassName,
+                                          std::string &ErrorMsg) {
+  if (!mUseStdout) {
+    mOF.clear();
+    std::string Path =
+        RSSlangReflectUtils::ComputePackagedPath(mOutputPath.c_str(),
+                                                 mPackageName.c_str());
+
+    if (!SlangUtils::CreateDirectoryWithParents(Path, &ErrorMsg))
+      return false;
+
+    std::string ClassFile = Path + "/" + ClassName + ".java";
+
+    mOF.open(ClassFile.c_str());
+    if (!mOF.good()) {
+      ErrorMsg = "failed to open file '" + ClassFile + "' for write";
+      return false;
+    }
+  }
+  return true;
+}
+
 const char *RSReflection::Context::AccessModifierStr(AccessModifier AM) {
   switch (AM) {
     case AM_Public: return "public"; break;
@@ -1789,6 +1783,10 @@
   if (mVerbose)
     std::cout << "Generating " << ClassName << ".java ..." << std::endl;
 
+  // Open file for class
+  if (!openClassFile(ClassName, ErrorMsg))
+    return false;
+
   // License
   out() << mLicenseNote;
 
diff --git a/slang_rs_reflection.h b/slang_rs_reflection.h
index b072790..c4750dc 100644
--- a/slang_rs_reflection.h
+++ b/slang_rs_reflection.h
@@ -33,6 +33,8 @@
 
     bool mVerbose;
 
+    std::string mOutputPath;
+
     std::string mInputRSFile;
 
     std::string mPackageName;
@@ -65,6 +67,9 @@
       return;
     }
 
+    bool openClassFile(const std::string &ClassName,
+                       std::string &ErrorMsg);
+
    public:
     typedef enum {
       AM_Public,
@@ -77,11 +82,13 @@
 
     static const char *AccessModifierStr(AccessModifier AM);
 
-    Context(const std::string &InputRSFile,
+    Context(const std::string &OutputPath,
+            const std::string &InputRSFile,
             const std::string &PackageName,
             const std::string &ResourceId,
             bool UseStdout)
         : mVerbose(true),
+          mOutputPath(OutputPath),
           mInputRSFile(InputRSFile),
           mPackageName(PackageName),
           mResourceId(ResourceId),
@@ -179,9 +186,6 @@
     inline void clearFieldIndexMap() { mFieldIndexMap.clear(); }
   };
 
-  bool openScriptFile(Context &C,
-                      const std::string &ClassName,
-                      std::string &ErrorMsg);
   bool genScriptClass(Context &C,
                       const std::string &ClassName,
                       std::string &ErrorMsg);
diff --git a/slang_utils.cpp b/slang_utils.cpp
new file mode 100644
index 0000000..d1955df
--- /dev/null
+++ b/slang_utils.cpp
@@ -0,0 +1,11 @@
+#include "slang_utils.h"
+
+#include "llvm/System/Path.h"
+
+using namespace slang;
+
+bool SlangUtils::CreateDirectoryWithParents(llvm::StringRef Dir,
+                                            std::string* Error) {
+  return !llvm::sys::Path(Dir).createDirectoryOnDisk(/* create_parents = */true,
+                                                     Error);
+}
diff --git a/slang_utils.h b/slang_utils.h
new file mode 100644
index 0000000..7fe5aac
--- /dev/null
+++ b/slang_utils.h
@@ -0,0 +1,22 @@
+#ifndef _SLANG_COMPILER_UTILS_H
+#define _SLANG_COMPILER_UTILS_H
+
+#include <string>
+
+namespace llvm {
+  class StringRef;
+}
+
+namespace slang {
+
+class SlangUtils {
+ private:
+  SlangUtils() {}
+
+ public:
+  static bool CreateDirectoryWithParents(llvm::StringRef Dir,
+                                         std::string* Error);
+};
+}
+
+#endif  // _SLANG_COMPILER_UTILS_H