diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py
index f60f95fd59e14de1b7eb33cf571fbb963f90e80f..9bb014c2dada4c155b93d7284110a31fbe4c99af 100644
--- a/pyopencl/__init__.py
+++ b/pyopencl/__init__.py
@@ -141,18 +141,28 @@ def _add_functionality():
                         routine=lambda : routine))
 
         if err is not None:
-            # Python 3.2 outputs the whole tree of currently active exceptions
+            # Python 3.2 outputs the whole list of currently active exceptions
             # This serves to remove one (redundant) level from that nesting.
             raise err
 
         message = (75*"="+"\n").join(
-                "Build on %s succeeded, but said:\n\n%s" % (dev, log) 
+                "Build on %s succeeded, but said:\n\n%s" % (dev, log)
                 for dev, log in self._get_build_logs()
                 if log is not None and log.strip())
 
         if message:
+            if self.kind() == program_kind.UNKNOWN:
+                build_type = "Build"
+            elif self.kind() == program_kind.SOURCE:
+                build_type = "From-source build"
+            elif self.kind() == program_kind.BINARY:
+                build_type = "From-binary build"
+            else:
+                raise RuntimeError("unexpected kind of program")
+
             from warnings import warn
-            warn("Build succeeded, but resulted in non-empty logs:\n"+message)
+            warn("%s succeeded, but resulted in non-empty logs:\n%s"
+                    % (build_type, message))
 
         return self
 
diff --git a/pyopencl/cache.py b/pyopencl/cache.py
index 7719c3a8da3d5f9c855384350f45b41a303d0f34..f0f3988b42c2710026252836d0ad3c6b2cac96e4 100644
--- a/pyopencl/cache.py
+++ b/pyopencl/cache.py
@@ -344,13 +344,14 @@ def _create_built_program_from_source_cached(ctx, src, options, devices, cache_d
             logs.append(log)
 
     message = (75*"="+"\n").join(
-            "Build on %s succeeded, but said:\n\n%s" % (dev, log) 
+            "Build on %s succeeded, but said:\n\n%s" % (dev, log)
             for dev, log in zip(devices, logs)
             if log is not None and log.strip())
 
     if message:
         from warnings import warn
-        warn("Build succeeded, but resulted in non-empty logs:\n"+message)
+        warn("Built kernel retrieved from cache. Original from-source build had warnings:\n"+message)
+
     # {{{ build on the build-needing devices, in one go
 
     result = None
diff --git a/src/wrapper/wrap_cl.hpp b/src/wrapper/wrap_cl.hpp
index fd1f947bcce35def42e8d89026762799b888ad45..78946cb82b5de5ab76727f7123856d3b87ad9d1e 100644
--- a/src/wrapper/wrap_cl.hpp
+++ b/src/wrapper/wrap_cl.hpp
@@ -2392,12 +2392,16 @@ namespace pyopencl
 
   class program : boost::noncopyable
   {
+    public:
+      enum program_kind_type { KND_UNKNOWN, KND_SOURCE, KND_BINARY };
+
     private:
       cl_program m_program;
+      program_kind_type m_program_kind;
 
     public:
-      program(cl_program prog, bool retain)
-        : m_program(prog)
+      program(cl_program prog, bool retain, program_kind_type progkind=KND_UNKNOWN)
+        : m_program(prog), m_program_kind(progkind)
       {
         if (retain)
           PYOPENCL_CALL_GUARDED(clRetainProgram, (prog));
@@ -2413,6 +2417,11 @@ namespace pyopencl
         return m_program;
       }
 
+      program_kind_type kind() const
+      {
+        return m_program_kind;
+      }
+
       npy_intp obj_ptr() const
       {
         return (npy_intp) data();
@@ -2560,7 +2569,7 @@ namespace pyopencl
 
     try
     {
-      return new program(result, false);
+      return new program(result, false, program::KND_SOURCE);
     }
     catch (...)
     {
@@ -2625,7 +2634,7 @@ namespace pyopencl
 
     try
     {
-      return new program(result, false);
+      return new program(result, false, program::KND_BINARY);
     }
     catch (...)
     {
diff --git a/src/wrapper/wrap_cl_part_2.cpp b/src/wrapper/wrap_cl_part_2.cpp
index 210f9510de2b4167c6c5b7336e4ccb6f33225451..c053e3df92bacc4dc45a66dcd632960f51d042cf 100644
--- a/src/wrapper/wrap_cl_part_2.cpp
+++ b/src/wrapper/wrap_cl_part_2.cpp
@@ -118,6 +118,12 @@ void pyopencl_expose_part_2()
   // {{{ program
   {
     typedef program cls;
+    py::enum_<cls::program_kind_type>("program_kind")
+      .value("UNKNOWN", cls::KND_UNKNOWN)
+      .value("SOURCE", cls::KND_SOURCE)
+      .value("BINARY", cls::KND_BINARY)
+      ;
+
     py::class_<cls, boost::noncopyable>("_Program", py::no_init)
       .def("__init__", make_constructor(
             create_program_with_source,
@@ -127,6 +133,7 @@ void pyopencl_expose_part_2()
             create_program_with_binary,
             py::default_call_policies(),
             py::args("context", "devices", "binaries")))
+      .DEF_SIMPLE_METHOD(kind)
       .DEF_SIMPLE_METHOD(get_info)
       .DEF_SIMPLE_METHOD(get_build_info)
       .def("_build", &cls::build,